Skip to content

Call operation performance optimizations #5537

Merged
shemnon merged 13 commits intohyperledger:mainfrom
shemnon:call-optimization
Aug 8, 2023
Merged

Call operation performance optimizations #5537
shemnon merged 13 commits intohyperledger:mainfrom
shemnon:call-optimization

Conversation

@shemnon
Copy link
Contributor

@shemnon shemnon commented Jun 5, 2023

PR description

  • Introduce "undoable" journoled collections: set, map, table, and list
  • Move values that are the same or shared across the transaction to
    TxValues record
  • Move warm and cold storage to undoable sets and tables.
  • Move transient storage to undoable table.
  • Move address hashing inside of address with a memoized field.
  • lazy create EOF return stack

Fixed Issue(s)

shemnon added 4 commits May 10, 2023 11:12
A series of collections that can be rolled back to a prior state.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
- Move values that are the same or shared across the transaction to
  TxValues record
- Move warm and cold storage to undoable sets and tables.
- Move transient storage to undoable table.
- Move address hashing inside of address with a memoized field.
- lazy create EOF return stack

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
@github-actions
Copy link

github-actions bot commented Jun 5, 2023

  • I thought about documentation and added the doc-change-required label to this PR if updates are required.
  • I thought about the changelog and included a changelog update if required.
  • If my PR includes database changes (e.g. KeyValueSegmentIdentifier) I have thought about compatibility and performed forwards and backwards compatibility tests

@shemnon
Copy link
Contributor Author

shemnon commented Jun 5, 2023

8%->80% performance improvement in the call/delegate call operation, respecitivly.

call

evm --repeat
1000
--code
6113376102005260005b61047c81101561050a5760206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af15060206101e0602061020060007372657475726e207465737420636f6e74726163745af150600181019050610009565b50

pre
Screenshot 2023-06-05 at 11 48 59 AM
post
Screenshot 2023-06-05 at 11 44 48 AM

delegate call

evm --repeat 1000 --code 61e11e6102805260005b61043e81101561050857602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450602061026060006102807372657475726e207465737420636f6e74726163745af450600181019050610009565b50

pre
Screenshot 2023-06-05 at 11 59 31 AM
post
Screenshot 2023-06-05 at 12 02 07 PM

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
/** The constant ZERO. */
public static final Address ZERO = Address.fromHexString("0x0");

static LoadingCache<Address, Hash> hashCache =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't want to add a limit for this cache ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What limit would you suggest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say a maximum size. Maybe we should see how much on average we load hash by block and got a number. But with a cache without max seems dangerous to me . Or else there is a default limit?


mark = subject.mark();
// non-existent remove doesn't advance mark
subject.remove("Bonjour");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🇫🇷

private Address address;
private final Supplier<Hash> addressHash =
Suppliers.memoize(() -> address == null ? Hash.ZERO : Hash.hash(address));
Suppliers.memoize(() -> address == null ? Hash.ZERO : address.addressHash());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it still need to have memoize in this case ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I applied it to all cases where we saw Hash.hsah(addres)) to be consistant.

@ahamlat
Copy link
Contributor

ahamlat commented Jun 6, 2023

8%->80% performance improvement in the call/delegate call operation, respecitivly.

When I look to the CPU profiling in case of Call operation, I see roughly the same percentage in samples pre and post this PR, around 63%. Is it the right screenshot ? Am I missing something ?

@ahamlat
Copy link
Contributor

ahamlat commented Jun 6, 2023

I'm still reviewing the PR, meanwhile I installed this PR on a Mainnet node to test the performance improvement compared to main branch

shemnon added 2 commits June 6, 2023 18:23
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
@shemnon
Copy link
Contributor Author

shemnon commented Jun 7, 2023

Assuming sampling is at the same fixed interval, the CALL samples had 3190 pre and 2943, so pre had 8.39% more samples. for DELEGATECALL the change was more dramantic at 4152/2249, so pre had 84.61% more samples. I'll get my CartEVM results tomorrow.

Total time decreased, not so much share of computation.

I also don't have a good "tower" call benchmark (where we do 1000 deep callstacks). I'll dig into reference tests for a good example. That is where I expect the most payback for undo collections.

What surprised me is just how much time is spent in the call frame init as opposed to other work steps.

@shemnon
Copy link
Contributor Author

shemnon commented Jun 7, 2023

I also want at a day of fuzzing before it gets committed.

  • Full day of fuzzing.

@ahamlat
Copy link
Contributor

ahamlat commented Jun 7, 2023

Assuming sampling is at the same fixed interval, the CALL samples had 3190 pre and 2943, so pre had 8.39% more samples. for DELEGATECALL the change was more dramantic at 4152/2249, so pre had 84.61% more samples. I'll get my CartEVM results tomorrow.

Total time decreased, not so much share of computation.

Got the idea, I'm used to profile on the same period, and in you case, the profiling was done during the test execution time.

I also don't have a good "tower" call benchmark (where we do 1000 deep callstacks). I'll dig into reference tests for a good example. That is where I expect the most payback for undo collections.

Please let me know if I can do the tests for you, I can launch some AWS instances and do whatever you want in terms of tests, I will just need some guidelines.

What surprised me is just how much time is spent in the call frame init as opposed to other work steps.

On Mainnet (without this PR), getCode takes most of the time when calculating the keccack hash of the code.

image

@shemnon
Copy link
Contributor Author

shemnon commented Jun 7, 2023

Tower call results are unimpressive, so given the breadth of the change I'm going to shelve it as not worth the risk.
Only 10% on calls...

./ethereum/evmtool/build/install/evmtool/bin/evm state-test ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json ethereum/referencetests/src/reference-test/external-resources/GeneralStateTests/stDelegatecallTestHomestead/Delegatecall1024.json

pre

{"output":"","gasUsed":"0x31e845","time":18264833,"Mgps":"179.072","test":"Delegatecall1024","fork":"Istanbul","d":0,"g":0,"v":0,"postHash":"0x5e34e5c4b1eec1379e8c442fcf05a38a557c319a5a0904c62e4a9071f9f5acbc","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x7c335","time":18224834,"Mgps":"27.914","test":"Delegatecall1024","fork":"London","d":0,"g":0,"v":0,"postHash":"0xfc071ef580274c1c82c28f60253849317c156f6159f711582ef93da01f26f884","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x7c335","time":18216584,"Mgps":"27.926","test":"Delegatecall1024","fork":"Merge","d":0,"g":0,"v":0,"postHash":"0xfc071ef580274c1c82c28f60253849317c156f6159f711582ef93da01f26f884","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x7c335","time":18320208,"Mgps":"27.769","test":"Delegatecall1024","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xfc071ef580274c1c82c28f60253849317c156f6159f711582ef93da01f26f884","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}

post

{"output":"","gasUsed":"0x31e845","time":16720084,"Mgps":"195.617","test":"Delegatecall1024","fork":"Istanbul","d":0,"g":0,"v":0,"postHash":"0x5e34e5c4b1eec1379e8c442fcf05a38a557c319a5a0904c62e4a9071f9f5acbc","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x7c335","time":17400291,"Mgps":"29.237","test":"Delegatecall1024","fork":"London","d":0,"g":0,"v":0,"postHash":"0xfc071ef580274c1c82c28f60253849317c156f6159f711582ef93da01f26f884","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x7c335","time":16167792,"Mgps":"31.465","test":"Delegatecall1024","fork":"Merge","d":0,"g":0,"v":0,"postHash":"0xfc071ef580274c1c82c28f60253849317c156f6159f711582ef93da01f26f884","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x7c335","time":16568500,"Mgps":"30.704","test":"Delegatecall1024","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xfc071ef580274c1c82c28f60253849317c156f6159f711582ef93da01f26f884","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}

Berlin is where access lists were introduced and is where the showdown is.

If actual on-chain gas limits are used (due to 63/64) stats are much better, but only a 70 high tower gets built - the reference test case Delegatecall1024OOG.json shows this

pre

{"output":"","gasUsed":"0x1aabcf","time":9169542,"Mgps":"190.622","test":"Delegatecall1024OOG","fork":"Berlin","d":0,"g":0,"v":0,"postHash":"0x2750ca6dc2a1b7bc15f2789a41bba800367457180d2bc620970d9d8f22b86939","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x233526","time":6176625,"Mgps":"373.564","test":"Delegatecall1024OOG","fork":"Istanbul","d":0,"g":0,"v":0,"postHash":"0x803f3435d98e5077d734e9cc87f1c7473634a9b9a12849894ea0231f9c4287fd","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x1aabcf","time":6702500,"Mgps":"260.786","test":"Delegatecall1024OOG","fork":"London","d":0,"g":0,"v":0,"postHash":"0xf0a9d54c9e9ae8a9500af004bc5a4bfaa136fef8b56f105d9db0530dedd23051","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x1aabcf","time":5973000,"Mgps":"292.637","test":"Delegatecall1024OOG","fork":"Merge","d":0,"g":0,"v":0,"postHash":"0xf0a9d54c9e9ae8a9500af004bc5a4bfaa136fef8b56f105d9db0530dedd23051","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x1aabcf","time":6280500,"Mgps":"278.309","test":"Delegatecall1024OOG","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xf0a9d54c9e9ae8a9500af004bc5a4bfaa136fef8b56f105d9db0530dedd23051","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}

post

{"output":"","gasUsed":"0x1aabcf","time":8405417,"Mgps":"207.951","test":"Delegatecall1024OOG","fork":"Berlin","d":0,"g":0,"v":0,"postHash":"0x2750ca6dc2a1b7bc15f2789a41bba800367457180d2bc620970d9d8f22b86939","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x233526","time":6424000,"Mgps":"359.179","test":"Delegatecall1024OOG","fork":"Istanbul","d":0,"g":0,"v":0,"postHash":"0x803f3435d98e5077d734e9cc87f1c7473634a9b9a12849894ea0231f9c4287fd","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x1aabcf","time":4924167,"Mgps":"354.967","test":"Delegatecall1024OOG","fork":"London","d":0,"g":0,"v":0,"postHash":"0xf0a9d54c9e9ae8a9500af004bc5a4bfaa136fef8b56f105d9db0530dedd23051","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x1aabcf","time":4532625,"Mgps":"385.631","test":"Delegatecall1024OOG","fork":"Merge","d":0,"g":0,"v":0,"postHash":"0xf0a9d54c9e9ae8a9500af004bc5a4bfaa136fef8b56f105d9db0530dedd23051","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}
{"output":"","gasUsed":"0x1aabcf","time":5300334,"Mgps":"329.775","test":"Delegatecall1024OOG","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xf0a9d54c9e9ae8a9500af004bc5a4bfaa136fef8b56f105d9db0530dedd23051","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true}

@shemnon
Copy link
Contributor Author

shemnon commented Jun 7, 2023

Address hash caching may be an independently useful optimization.

@shemnon shemnon marked this pull request as draft June 7, 2023 17:07
shemnon added 5 commits July 28, 2023 17:12
Merge main, post 4844 changes

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
@shemnon
Copy link
Contributor Author

shemnon commented Aug 1, 2023

Performance numbers

  • "all in" performance is a small drop within statistical noise. data
  • performacne of CALL, DELEGATECALL, and STATICCALL improve 20%-26% data
  • Selfdestruct takes a nearly 80% hit, but that is from an absurd 5.9 GigaGas/sec to 1.3 GigaGas/sec data

Call ops are still the worst performers, so raising the floor is worth the hit to a lesser used top performing operation.

@shemnon shemnon marked this pull request as ready for review August 1, 2023 03:46
@shemnon
Copy link
Contributor Author

shemnon commented Aug 7, 2023

A week of fuzzing had no findings

INFO [08-07|12:48:59.102] Executing                                tests=6,739,367 time=156h0m40.003s test/s=12.0 "avg steps"=4310.0 global=6,961,318

Copy link
Contributor

@daniellehrner daniellehrner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@shemnon shemnon merged commit fb3b262 into hyperledger:main Aug 8, 2023
elenduuche pushed a commit to elenduuche/besu that referenced this pull request Aug 16, 2023
* Call Operation performance improvements

- Move values that are the same or shared across the transaction to
  TxValues record
- Move warm and cold storage to undoable sets and tables.
- Move transient storage to undoable table.
- Move address hashing inside of address with a memoized field.
- lazy create EOF return stack

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
garyschulte pushed a commit to garyschulte/besu that referenced this pull request Aug 28, 2023
* Call Operation performance improvements

- Move values that are the same or shared across the transaction to
  TxValues record
- Move warm and cold storage to undoable sets and tables.
- Move transient storage to undoable table.
- Move address hashing inside of address with a memoized field.
- lazy create EOF return stack

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
garyschulte pushed a commit to garyschulte/besu that referenced this pull request Aug 28, 2023
* Call Operation performance improvements

- Move values that are the same or shared across the transaction to
  TxValues record
- Move warm and cold storage to undoable sets and tables.
- Move transient storage to undoable table.
- Move address hashing inside of address with a memoized field.
- lazy create EOF return stack

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
garyschulte pushed a commit to garyschulte/besu that referenced this pull request Aug 28, 2023
* Call Operation performance improvements

- Move values that are the same or shared across the transaction to
  TxValues record
- Move warm and cold storage to undoable sets and tables.
- Move transient storage to undoable table.
- Move address hashing inside of address with a memoized field.
- lazy create EOF return stack

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
eum602 pushed a commit to lacchain/besu that referenced this pull request Nov 3, 2023
* Call Operation performance improvements

- Move values that are the same or shared across the transaction to
  TxValues record
- Move warm and cold storage to undoable sets and tables.
- Move transient storage to undoable table.
- Move address hashing inside of address with a memoized field.
- lazy create EOF return stack

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants