With the quadratic call-site propagation removed (#1995, PR #2006), the remaining compile time on a function-heavy file is split roughly evenly between type checking and codegen, and both still grow faster than linearly.
Measurement
One function per call site, best of 7 runs each. Phases separated with the compiler's own flags: --dump-ast is lex+parse, --check is through type checking, no flag is the whole compile.
| functions |
parse |
type check |
codegen |
whole compile |
| 400 |
0.016s |
0.004s |
0.008s |
0.028s |
| 800 |
0.019s |
0.018s |
0.024s |
0.061s |
| 1600 |
0.033s |
0.072s |
0.081s |
0.185s |
Growth per doubling of input:
| phase |
400 to 800 |
800 to 1600 |
| parse |
1.2x |
1.7x |
| type check |
4.5x |
4.0x |
| codegen |
3.0x |
3.4x |
Parse is linear. Type checking and codegen are not.
For contrast, the same table before #1995 was fixed: type check 0.028 / 0.090 / 0.326, whole compile 0.052 / 0.129 / 0.461. So #1995 removed the largest term, and 4.5x-per-doubling is what is left underneath it.
Why it is worth an issue rather than nothing
Same shape of argument as #1995. The 50000-token source cap currently bounds the input, so this is a scaling hazard rather than a live complaint, and the constants are small enough that the largest module in the tree (3472 lines) compiles in about 1.4s. Anyone who raises that cap, or lands a generated module, inherits the growth.
It is filed separately because it is two different passes with two different causes, and finding them starts with a profile rather than a code reading. #1995's cause was visible in the loop nesting; nothing here is.
Reproducing
n=1600
awk -v n=$n 'BEGIN{
for(i=0;i<n;i++) printf "f%d(a) {\n return a + %d;\n}\n", i, i;
print "main() {"; print " t = 0;";
for(i=0;i<n;i++) printf " t = t + f%d(%d);\n", i, i;
print " print(t);"; print " print(\"\\n\");"; print "}" }' > /tmp/s.ae
build/aetherc --dump-ast /tmp/s.ae /tmp/out.c # parse
build/aetherc --check /tmp/s.ae /tmp/out.c # through type check
build/aetherc /tmp/s.ae /tmp/out.c # whole compile
3200 is not measurable: the file hits the 50000-token cap first.
Found while verifying the fix for #1995.
With the quadratic call-site propagation removed (#1995, PR #2006), the remaining compile time on a function-heavy file is split roughly evenly between type checking and codegen, and both still grow faster than linearly.
Measurement
One function per call site, best of 7 runs each. Phases separated with the compiler's own flags:
--dump-astis lex+parse,--checkis through type checking, no flag is the whole compile.Growth per doubling of input:
Parse is linear. Type checking and codegen are not.
For contrast, the same table before #1995 was fixed: type check 0.028 / 0.090 / 0.326, whole compile 0.052 / 0.129 / 0.461. So #1995 removed the largest term, and 4.5x-per-doubling is what is left underneath it.
Why it is worth an issue rather than nothing
Same shape of argument as #1995. The 50000-token source cap currently bounds the input, so this is a scaling hazard rather than a live complaint, and the constants are small enough that the largest module in the tree (3472 lines) compiles in about 1.4s. Anyone who raises that cap, or lands a generated module, inherits the growth.
It is filed separately because it is two different passes with two different causes, and finding them starts with a profile rather than a code reading. #1995's cause was visible in the loop nesting; nothing here is.
Reproducing
3200 is not measurable: the file hits the 50000-token cap first.
Found while verifying the fix for #1995.