Skip to content

Qi Compiler Sync Jan 26 2023

Siddhartha Kasivajhula edited this page Mar 3, 2023 · 7 revisions

A Loophole in Qi Space

Qi Compiler Sync Jan 26 2023

Adjacent meetings: Previous | Up | Next

Summary

We continued restoring the performance of Qi forms to pre-stratification levels, defining functions in the Qi binding space as a way to avoid either inflating the core language or adding new extended syntax (macros).

Background

Some Qi forms are still slower than they were prior to stratifying the implementation into separate expansion and compilation stages. We are interested in restoring their performance prior to merging the compiler work to the main line of development. To aid in assessing performance regression, the form benchmark reporting script was modified to accept a file representing reference results to compare against, and return relative performance instead of absolute time values.

Amp

Last time, we found that amp, while being a core (rather than extended) form, and while having an implementation identical to the original one, was still slower. Upon further investigation, this turned out to be statistical, and the performance is actually the same as a priori performance. So it turns out there were two wild geese running amok last time.

count and live?

These were about 50X slower than before. One option was to reinstate them as core forms and restore their original optimized implementations, but this would have the drawback of inflating the size of the core language. A second option formerly considered was to write them as extended forms (macros) but annotate them with metadata during expansion to allow performance to be restored via a compiler optimization. This option was discarded since we agreed that it was equivalent to expanding the core language but in a disguised way. A third option was to write them as extended forms (macros) that rewrite to internal helper functions in the definition-site scope. This has the drawback that these macros would be "cheating" by rewriting to the esc form, i.e. by being essentially embedded Racket macros (Racket inside Qi inside Racket 😵‍💫).

Instead, we took the approach of defining these as "Qi functions."

Qi functions

Qi functions are functions defined in a Qi binding space. They are not Qi forms, but rather, function identifiers just like any functions defined using Racket, except that they take precedence over host language identifiers when these both exist in scope. This allows such functions to have privileged status and optimized implementations without officially being part of the Qi language - a "loophole in Qi space."

Implications

As these functions are not part of the core language, they cannot be part of Qi's theory of optimization, since the compiler will not have access to the full semantics of these functions and can only optimize them in a generic way.

On the other hand, for standard Racket library functions, there is already one planned way in which they can be minimally part of optimizations -- by building a table of known arities for use in optimization rules. We could employ a similar approach for such Qi functions, and it's possible that no more-specialized optimizations would be called for. If this doesn't turn out to be enough, these could of course be incorporated as core forms at that stage.

In general, some forms have to be in the core language since they entail specialized scoping rules (like ~>) or require non-local compilation (like as). Forms like live? don't have such hard constraints, and this gives us some flexibility and the ability to re-assess their handling as needs become apparent.

AND and all?

These were about 12X slower. We restored optimized implementations for them in the same way, by converting them to Qi functions.

Current Performance Regression Report

This can be run at the command line as:

$ cd qi-sdk/profile
$ ./report.rkt -r path/to/before.json
((OR 15.04)
 (any? 14.13)
 (none? 13.81)
 (NOR 7.44)
 (any 5.44)
 (none 4.31)
 (partition 2.45)
 (all 1.79)
 (or% 1.72)
 (feedback 1.72)
 (thread 1.72)
 (fanout 1.7)
 (loop 1.67)
 (pass 1.61)
 ((require qi) 1.56)
 (relay 1)
 (not 1)
 (NAND 1)
 (switch 1)
 (bundle 1)
 (unless 1)
 (amp 1)
 (crossover 1)
 (ground 1)
 (count 1)
 (select 1)
 (all? 1)
 (XOR 1)
 (inverter 1)
 (input aliases 1)
 (catchall-template 1)
 (gate 1)
 (gen 1)
 (loop2 1)
 (AND 1)
 (NOT 1)
 (XNOR 1)
 (when 1)
 (one-of? 1)
 (tee 1)
 (collect 1)
 (>> 1)
 (sieve 1)
 (apply 1)
 (template 1)
 (block 1)
 (<< 1)
 (group 1)
 (and% 1)
 (rectify 1)
 (live? 1)
 (if 1)
 (and 1)
 (effect 1)
 (sep 1)
 (relay* 1)
 (try 1)
 (esc 1)
 (clos 1)
 (or 1)
 (thread-right 0.69)
 (currying 0.54))

Next Steps

(Some of these are carried over from last time)

  • Continue restoring performance of other slow forms.
  • Write non-local benchmarks to track the effect of compiler optimizations soon to be written.
  • Add docs for syntax-spec and put it on the package index.
  • Review the initial design for bindings and see what other bindings cases we could support next.

Attendees

Hans, Michael, Nia, Sid

Clone this wiki locally