-
Notifications
You must be signed in to change notification settings - Fork 30
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
Make LLVM's stack growable #644
Merged
Merged
Changes from 1 commit
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
0e98c1d
Grow stack and make it smaller
marvinborner 5235f07
Remove redundant comment
marvinborner 4650a52
Merge branch 'master' into feature/llvm-stack-growing
b-studios 8c8daab
Round to next power of two
phischu aec52d0
Start small
phischu 5dfb7af
Move base and base_pointer
marvinborner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As someone who doesn't understand the LLVM runtime, why do we need to add the
%n
here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not strictly related to the runtime, but rather the growing logic. Since the bytes to be allocated (
n
) could be larger than the double of the current stack size, I first double the size and then addn
to it. We could (should?) of course do something smarter here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intuitively, I'd expect something like "if we know we have to resize, then
new_size <- nextStrictlyBiggestPowerOfTwo(current_size, n)
" where the mysterious function is https://llvm.org/doxygen/namespacellvm.html#afb65eef479f0473d0fe1666b80155237 orclz
(get the highest bit, choose a number one bigger in binary), but I have to think about it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that also sounds good. My thought behind the strategy was that, intuitively, large stack allocations are quite rare (or, in effekt's case, impossible?). So hypothetically, if I allocate 1GB on a 10MB stack, I'd prefer the next stack size to be 20MB + 1GB rather than 2GB.
In hindsight this hypothetical doesn't really make sense because we only use this function to allocate really small sizes 🤷♂️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth looking at
https://github.com/golang/go/blob/24cb743d1faa6c8f612faa3c17ac9de5cc385832/src/runtime/stack.go#L336
and it's callsites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about it for a bit and something like the following should be pretty good:
Rationale:
Here's my crappy LLVM impl:
Of course, perhaps I'm really overthinking this, I'd really need to benchmark.
Also, I'm not sure whether we should do
size * 2
,size * 1.5
orsize * <golden ratio>
. Probably again needs to be benchmarked.To continue thinking about this, it would be really nice to know the "profile" of allocations: how do our allocations actually look like? Can we serialise this somehow and then read later?
(see summary below where I discover that I have no clue how to model this)
WDYT?
btw, @jiribenes tried to do statistics here
I also thought about my assumptions:
but when I try to even simulate them, I can clearly see that they are not true, since they result in very whacky stacks with the function I suggested under the distribution above:
Of course, my code is most likely bad, but this is not very encouraging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very interesting, although I agree that maybe you're overthinking this a bit
Regarding the profile of the allocations: We (currently?) only use
@stackAllocate
to make space for pushes of pos/neg, builtin types and other pointers, never for arbitrarily large data allocations. (Maybe @phischu can confirm this?)As far as I understand, large (de-)allocations only happen when some function has a lot of arguments that need to be stored on the stack (~8-16 byte each). In
nqueens
, for example, this leads to stack (de-)allocations of 108 bytes.To make this more clear: Allocation sizes/amount in all generated files in
effekt.llvmtests
:So I don't think we really need to be creative with the growing logic, since
%n
will almost never be larger than the doubled current stack size, especially if we settle on an initial stack size like 1024. Minimal solutions like adding the current size (as I did), or by usingNextPowerOf2
are probably more than enough.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@phischu asked about the stack allocation profile at runtime. Here's the data:
For the entire test suite (
test
):Only the LLVM tests (
testOnly effekt.LLVMTests
):