-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Experimental: Add Slice.literal
for numeric slice constants
#13716
Experimental: Add Slice.literal
for numeric slice constants
#13716
Conversation
Times on my x86-64 Debian machine, also relative to an empty source:
Times on x86-64 Windows:
|
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.
LGTM 🚀
I'm wondering if |
Inspired by #13339, this PR adds a
Slice.literal
method that constructsSlice
constants of numeric elements in a program's read-only data section directly. String-like literals such as those suggested in #2886 can be implemented on top of this with user-land macros or new literal expansions. Resolves part of #5792 (there is no way to create read-onlyHash
es from these literals). Does not affect #2485 and #9486 (these use cases require a mutable data structure).The call:
expands to:
Currently
Slice(T).literal
is marked as experimental and offers only a minimally sufficient API.T
must be explicitly specified, must be one of the primitive number types, and cannot be a union. All elements must be number literals fitting intoT
's range.The literals are implemented via a new
@[Primitive(:slice_literal)]
which performs the above transformation in the semantic phase; the codegen phase never sees theslice_literal
primitive, and only needs to define the global LLVM symbols corresponding to those slice contents. The above will emit:One goal of such literals is to reduce compilation times of huge constant arrays without resorting to workarounds like #5792 (comment). The following snippet generates two files, one with a 50,000-element array literal, another with a 50,000-element
Slice.literal
: (we don't want to measure macro expansion overhead here, so we won't express those literals as macro for-loops)The two files are compiled with and without release mode, and the four
--stats
, each relative to an empty source file with the same compilation settings, are shown below. Only compilation phases with significant time changes are shown. All times are collected on an Apple M2 with a non-release build of the compiler with this PR applied.cache_array.cr
w/o
--release
cache_literal.cr
w/o
--release
cache_array.cr
with
--release
cache_literal.cr
with
--release
From this we could conclude:
Call
s are more complex thanArrayLiteral
s.O(number of elements)
AST nodes, which the literal expander does for array literals. This saves memory too. (For the same reason,Slice.literal
must not be a user-land macro, as that would also interpolateO(n)
nodes.)alloca
s per element.cache_literal.cr
is almost indistinguishable from an empty file. This is again due to eliminating thealloca
s.alloca
s as well. Presumably some bad inlining makes release modecache_array.cr
far slower than the other three builds.As a side effect,
Slice(T)
is now available in the empty prelude because of the primitive. It is not a built-in type yet; in particular,@[Primitive(:slice_literal)]
does not assume any specific layout fromSlice
, since only the contents are stored in read-only memory, not theSlice
s themselves. (Contrast with #12020 whereString
's layout is hardcoded in multiple places.) It does expect a specific constructor signature though, and one such as below is required for an empty prelude:The interpreter does not support these literals yet.