Make JetStream 2
[WebKit-https.git] / PerformanceTests / JetStream2 / ARES-6 / Air / README.md
1 # All about Air.js
2
3 Air.js is an ES6 benchmark. It tries to faithfully use new features like arrow
4 functions, classes, for-of, and Map/Set, among others. Air.js doesn't avoid any
5 features out of fear that they might be slow, in the hope that we might learn
6 how to make those features fast by looking at how Air.js and other benchmarks
7 use them.
8
9 This documents the motivation, design, and license of Air.js.
10
11 To run Air.js, simply open "[Air.js/test.html](test.html)" in your browser. It
12 will only run correctly if your browser supports ES6.
13
14 ## Motivation
15
16 At the time that Air.js was written, most JavaScript benchmarks used ES5 or
17 older versions of the language. ES6 testing mostly relied on microbenchmarks or
18 conversions of existing tests to ES6. We try to use larger benchmarks to avoid
19 over-optimizing for small pieces of code, and we avoid making changes to
20 existing benchmarks because that approach has no limiting principle: if it's OK
21 to change a benchmark to use a feature, does that mean we can also change it to
22 remove the use of a feature we don't like? We feel that the best way to avoid
23 falling into the trap of creating benchmarks that reinforce what some JS engine
24 is already good at is to create a new benchmark from first principles.
25
26 We only recently completed our new JavaScript compiler, called
27 [B3](https://webkit.org/blog/5852/introducing-the-b3-jit-compiler/). B3's
28 backend, called
29 [Air](https://webkit.org/docs/b3/assembly-intermediate-representation.html), is
30 very CPU-intensive and uses a combination of object-oriented and functional
31 idioms in C++. Additionally, it relies heavily on high speed maps and sets. It
32 goes so far as to use customized map/set implementations - even more so than
33 the rest of WebKit. This makes Air a great candidate for ES6 benchmarking.
34 Air.js is a faithful ES6 implementation of Air. It pulls no punches: just as
35 the original C++ Air was written with expressiveness as a top priority, Air.js
36 is liberal in its use of modern ES6 idioms whenever this helps make the code
37 more readable. Unlike the original C++ Air, Air.js doesn't exploit a deep
38 understanding of compilers to make the code easy to compile.
39
40 ## Design
41
42 Air.js runs one of the more expensive Air phases, Air::allocateStack(). This
43 turns abstract stack references into concrete stack references, by selecting
44 how to lay out stack slots in the stack frame. This requires liveness analysis
45 and an interference graph.
46
47 Air.js relies on three major ES6 features more so than most of the others:
48
49 - Arrow functions. Like the C++ Air, Air.js uses a functional style of
50   iterating most non-trivial data-structures:
51
52         inst.forEachArg((arg, role, type, width) => ...)
53   
54   This is because the functional style allows the callbacks to mutate the data
55   being iterated: if the callback returns a non-null value, forEachArg() will
56   replace the argument with that value. This would not have been possible with
57   for-of.
58
59 - For-of. Many Air data structures are amenable to for-of iteration. While the
60   innermost loops tend to use functional iteration, pretty much all of the
61   outer logic uses for-of heavily. For example:
62
63         for (let block of code) // Iterate over the basic blocks
64             for (let inst of block) // Iterate over the instructions in a block
65                 ...
66
67 - Map/Set. The liveness analysis and Air::allocateStack() rely on maps and
68   sets. For example, we use a liveAtHead map that is keyed by basic block. Its
69   values are sets of live stack slots. This is a relatively crude way of doing
70   liveness, but it is exactly how the original Air::LivenessAnalysis worked, so
71   we view it as being quite faithful to how a sensible programmer might use Map
72   and Set.
73
74 Air.js also uses some other ES6 features. For example, it uses a Proxy
75 in one place, though we doubt that it's on a critical path. Air.js uses classes
76 and let/const extensively, as well a symbols. Symbols are used as enumeration
77 elements, and so they frequently show up as cases in switch statements.
78
79 The workflow of an Air.js run is pretty simple: we do 150 runs of allocateStack
80 on four IR payloads.
81
82 Each IR payload is a large piece of ES6 code that constructs an Air.js Code
83 object, complete with blocks, temporaries, stack slots, and instructions. These
84 payloads are generated by running Air::dumpAsJS() phase just prior to the
85 native allocateStack phase on the largest hot function in four major JS
86 benchmarks according to JavaScriptCore's internal profiling:
87
88 - Octane/GBEmu, the executeIteration function.
89 - Kraken/imaging-gaussian-blur, the gaussianBlur function.
90 - Octane/Typescript, the scanIdentifier function,
91 - Air.js, an anonymous closure identified by our profiler as ACLj8C.
92
93 These payloads allow Air.js to precisely replay allocateStack on those actual
94 functions.
95
96 It was an a priori goal of Air.js to spend most of the time in the
97 allocateStack phase. This is a faithful reproduction of the C++ allocateStack
98 phase, including its use of an abstract liveness analysis. It's abstract in the
99 sense that the same liveness algorithm can be reused for temporaries,
100 registers, or stack slots. In C++ this meant using templates, while in ES6 it
101 means more run-time dynamic dispatch.
102
103 Each IR payload is executable code that allocates the IR, and about 15% of
104 benchmark execution time is spent in that code. This is significant, but having
105 learned this, we don't feel that it would be honest to try to change the
106 efficiency of payload initialization. What if the payload initialization was
107 more expensive on our engine than others? If it was, then such a change would
108 not be fair.
109
110 Air.js validates its results. We added a Code hashing capability to both the
111 C++ Air and Air.js, and we assert each payload looks identical after
112 allocateStack to what it would have looked like after the original C++
113 allocateStack. We also validate that payloads hash properly before
114 allcoateStack, to help catch bugs during payload initialization. We have not
115 measured how long hashing takes, but it's a O(N) operation, while allocateStack
116 is closer to O(N^2). We suspect that barring some engine pathologies, hashing
117 should be much faster than allocateStack, and allocateStack should be where the
118 bulk of time is spent.
119
120 ## License
121
122 Copyright (C) 2016 Apple Inc. All rights reserved.
123
124 Redistribution and use in source and binary forms, with or without
125 modification, are permitted provided that the following conditions
126 are met:
127
128 1. Redistributions of source code must retain the above copyright
129    notice, this list of conditions and the following disclaimer.
130
131 2. Redistributions in binary form must reproduce the above copyright
132    notice, this list of conditions and the following disclaimer in the
133    documentation and/or other materials provided with the distribution.
134
135 THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
136 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
137 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
138 PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
139 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
140 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
141 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
142 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
143 OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
144 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
145 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
146
147 ## Summary
148
149 At the time that Air.js was written, we weren't happy with the ES6 benchmarks
150 that were available to us. Air.js uses some ES6 features in anger, in the hope
151 that we can learn about possible optimization strategies by looking at this and
152 other benchmarks.