Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

abigen generated bindings can be optimized by parsing abi once during an init function #22269

Closed
bonedaddy opened this issue Feb 5, 2021 · 3 comments · Fixed by #22583
Closed

Comments

@bonedaddy
Copy link

Rationale

In heavy usage environments which frequently create temporary bindings for usage during a small period of time, a lot of time is spent running abi.JSON. For example I have a backend microservice that listens for new blocks to be mined, and when new blocks are mined instantiates a variety of different contract bindings.

While profiling this program, 7.2% of the recorded profile was spend running abi.JSON. Because the abi package uses reflection, this can lead to a lot of time parsing ABI definitions. Because thes ABI definitions are declared as const variables, a better solution would be to have an init function that does the ABI parsing once when the corresponding package is initialized.

There are some optimizations a user can implement in their programs, such as attempting to make instantiations of the contract bindings longer lived, however this is not always possible.

Implementation

For the sake of arguments lets consider generating bindings for an ERC20 contract. Each time a binding is instantiated the bindERC20 function is called:

// bindErc20 binds a generic wrapper to an already deployed contract.
func bindErc20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
	parsed, err := abi.JSON(strings.NewReader(Erc20ABI)) // <---- lots of reflection use <----
	if err != nil {
		return nil, err
	}
	return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}

This logic could be reworked such that abi.JSON is moved to an init function, and the program structure could be rewritten to:

var (
    parsedABI abi.ABI
)

func init() {
    var err error
    parsedABI, err = abi.JSON(strings.NewReader(Erc20ABI))
    if err != nil {
        panic(err) // maybe not use panic?
    }
}

// bindErc20 binds a generic wrapper to an already deployed contract.
func bindErc20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
	return bind.NewBoundContract(address, parsedABI, caller, transactor, filterer), nil
}

I haven't done a lot of work with Golang's templating engine, but I would be willing to take a stab at implementing this

@MariusVanDerWijden
Copy link
Member

Sounds like a reasonable request to me, What do you think @gballet ?

@MariusVanDerWijden
Copy link
Member

@bonedaddy Would you like to try it?

@bonedaddy
Copy link
Author

bonedaddy commented Feb 16, 2021

@MariusVanDerWijden absolutely! I'll have some time later in the week to take a stab at this.

edit:

Apologies been a bit busier than expected should get around to this shortly 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants