Conversation
At every API call utils.FromInterface method was called. This method always allocates new *big.Int, which is very stressful for the runtime. Instead, we first check if the variable is already *big.Int and return it otherwise. Only if type assertion fails do we allocate new *big.Int. Now, as we do not allocate every time, we had to change the API calls to allocate a new result, so that we wouldnt mutate the passed in values.
| return b | ||
| func (e *engine) toBigInt(i1 frontend.Variable) *big.Int { | ||
| switch vv := i1.(type) { | ||
| case *big.Int: |
There was a problem hiding this comment.
shouldn't we still do a mod reduce if it's a big int? may slow things down (or we could have a fast path with a comparaison first) --> you can have circuit inputs / constants at this stage, so it may trigger weird edge cases if we allow some values in the test engine to not be mod reduced?
There was a problem hiding this comment.
I tried to rewrite the API methods such that toBigInt is only called for method inputs and I always recreate a new *big.Int for temp variables and result. The operations and results are always mod reduced.
I also tried to mod reduce the inputs in toBigInt method, but as I will be modifying inputs, I started having a few failing edge cases for the integration tests. Another approach would be to allocate a new *big.Int, set its value from the input and mod reduce it, but this goes against the goal of this PR which was to prevent unnecessary allocations.
At every API call utils.FromInterface method was called. This method always
allocates new *big.Int, which is very stressful for the runtime. Instead, we
first check if the variable is already *big.Int and return it otherwise. Only
if type assertion fails do we allocate new *big.Int.
Now, as we do not allocate every time, we had to change the API calls to
allocate a new result, so that we wouldn't mutate the passed in values.