Skip to content

feat[codegen]: deallocate variables after last use#4219

Closed
charles-cooper wants to merge 18 commits intovyperlang:masterfrom
charles-cooper:feat/mem-dealloc
Closed

feat[codegen]: deallocate variables after last use#4219
charles-cooper wants to merge 18 commits intovyperlang:masterfrom
charles-cooper:feat/mem-dealloc

Conversation

@charles-cooper
Copy link
Copy Markdown
Member

@charles-cooper charles-cooper commented Sep 2, 2024

deallocate variables after last use, improving memory usage.

What I did

How I did it

How to verify it

Commit message

Commit message for the final, squashed PR. (Optional, but reviewers will appreciate it! Please see our commit message style guide for what we would ideally like to see in a commit message.)

Description for the changelog

Cute Animal Picture

image

deallocate variables after last use, improving memory usage.
@charles-cooper charles-cooper changed the title feat[codegen]: deallocate variables after last-use feat[codegen]: deallocate variables after last use Sep 2, 2024
@charles-cooper charles-cooper added this to the v0.4.1 milestone Sep 2, 2024
@charles-cooper
Copy link
Copy Markdown
Member Author

charles-cooper commented Sep 2, 2024

marked this for inclusion in 0.4.1, but depending on when our next audit can be, might push to 0.4.2

use_count inside of Expr is dangerous, since Expr() can be called
multiple times for a given node. separate the analysis.
Comment thread vyper/codegen/context.py
var = self.vars[varname]
for s in self._scopes:
if s not in var.blockscopes:
# defer deallocation until we hit the end of its scope
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is this accurate? we defer the dealoc until we hit its scope again (not necessarily its end)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

or if "it" refers to the scope then still we might have to wait until multiple scopes finish

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

"it" refers to the scope of the variable

Copy link
Copy Markdown
Collaborator

@cyberthirst cyberthirst Sep 17, 2024

Choose a reason for hiding this comment

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

but we sweep after statements, right?

a: uint256 = 0
for i: uint256 in range(1, 3):
  a += i # <- can't dealoc, defer
b: uint256 = 0 # a already deallocated, swept after the for
b += 1
return b  # actual scope end but a is already deallocated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yea, this is the idea here

Comment thread vyper/codegen/context.py Outdated
@cyberthirst
Copy link
Copy Markdown
Collaborator

Given that we log write as both write+read, the dealoc won't happen until the last write (although the memory variable might be long dead at that point). So, in this context, the write operation is considered as "use".

@charles-cooper
Copy link
Copy Markdown
Member Author

Given that we log write as both write+read, the dealoc won't happen until the last write (although the memory variable might be long dead at that point). So, in this context, the write operation is considered as "use".

i think a write is a use

@charles-cooper
Copy link
Copy Markdown
Member Author

closing and reopening to try to fix the codecov report

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 21, 2025

Codecov Report

Attention: Patch coverage is 95.65217% with 2 lines in your changes missing coverage. Please review.

Project coverage is 92.24%. Comparing base (ded6b2c) to head (742fc7e).
Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
vyper/codegen/context.py 89.47% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4219      +/-   ##
==========================================
+ Coverage   92.16%   92.24%   +0.07%     
==========================================
  Files         122      122              
  Lines       17471    17504      +33     
  Branches     2959     2967       +8     
==========================================
+ Hits        16103    16146      +43     
+ Misses        957      951       -6     
+ Partials      411      407       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@charles-cooper charles-cooper modified the milestones: v0.4.1, v0.4.2 Mar 10, 2025
Comment thread vyper/codegen/expr.py

last_use = self.expr._metadata.get("last_use", False)
if last_use and var.location == MEMORY:
self.context.mark_for_deallocation(varname)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what if the variable is entirely unused? then we won't have the use to deallocate it

@cyberthirst
Copy link
Copy Markdown
Collaborator

Given that we log write as both write+read, the dealoc won't happen until the last write (although the memory variable might be long dead at that point). So, in this context, the write operation is considered as "use".

i think a write is a use

so that we don't have to deal with side-effects?

@cyberthirst
Copy link
Copy Markdown
Collaborator

reminder to run this against https://github.com/pcaversaccio/snekmate gas snapshots to consider whether the inclusion of this PR is worth it

@charles-cooper charles-cooper added the release - tentative items still being considered for release inclusion label Mar 17, 2025
Comment thread vyper/codegen/context.py Outdated
if varname in self.forvars:
return
if self.vars[varname].system:
return
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

As far as I can determine, this line cannot be covered because builtins such as sqrt are not analyzed for last use.
Also a comment for why these variables are not marked for deallocation (such as the one for forvars) would be nice.

info = expr._expr_info
if info is None:
continue
for r in info._reads:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not asking for a change, but I found it a bit confusing that _reads also include writes.

Comment thread vyper/codegen/expr.py
@@ -182,8 +182,14 @@ def parse_Name(self):

# local variable
if varname in self.context.vars:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not really related to this PR, but self.context.vars actually contains varnames which than have to be mapped to actual vars. Perhaps it should be renamed to reflect this (as in self.context.varnames) in the future.

@vyperlang vyperlang deleted a comment from evintila Oct 1, 2025
@charles-cooper
Copy link
Copy Markdown
Member Author

obsolesced by #4756

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

Labels

release - tentative items still being considered for release inclusion

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants