Professional Documents
Culture Documents
Comparing Rapid Type Analysis With Points-To Analy
Comparing Rapid Type Analysis With Points-To Analy
B. Tizer in [24]. On top of that, we also develop a parallel and compilation of Java. We discuss the effects on analysis
incremental version of the analysis. The incrementality is time, build time, reachable elements, and binary size.
achieved by using method summaries that sum up the effect We also evaluate the scalability of both analysis meth-
of each analyzed method. These summaries can be serialized ods. The results show that for bigger applications the
and reused between multiple builds. analysis time can be reduced by up to 64 %.
RTA can provide results quicker at the cost of reduced pre-
cision. The lower precision can yield bigger binaries, which 2 Overview of GraalVM Native Image
is not so problematic during the development phase. On the
other hand, the need to compile more classes and methods GraalVM Native Image [26] produces standalone binaries
goes against the savings due to the cheaper analysis. We for Java applications that contain the application along with
try to answer the research question whether such a loss of all dependencies and necessary runtime components such
precision is justified. We perform an extensive experimental as the garbage collector and threading support. It relies on a
closed-world assumption, i.e., all code is available to analyze
comparison of our version of RTA and the PTA currently
implemented in GraalVM Native Image to see whether RTA at image build time. Dynamic features such as reflection and
can provide some advantage over the PTA, and if so, how dynamic class loading are supported by explicitly registering
much. the program elements that would otherwise be opaque to
For our experiments, we use the standard Java benchmark the analysis. The image build process consists of several
successive phases and subphases.
suites Renaissance [15] and Dacapo [7] along with example
First, the points-to analysis is started to detect reachable
applications for the Java microservice frameworks Spring
[25], Micronaut [12], and Quarkus [17]. The experimental program elements. It starts with a set of root methods, which
evaluation shows, for example, that RTA can reduce the includes the application entry point specified by the user
analysis time of Spring Petclinic—a popular demo application as well as the entry points of runtime components. The
of the Spring framework—by 64 % and the overall build time execution of the analysis is interconnected with application
initialization and heap snapshotting.
by 35 % at the cost of increasing the image size by 15 %. On
Application initialization at build time allows developers
average, RTA reduced the analysis time by 40 % and the
overall build time by 15 % at the cost of increasing the image to initialize parts of their application when the image is be-
size by 15 %1 . ing built instead of performing the initialization at every
We also experiment with the scalability of both RTA and application startup. During the initialization, static fields of
PTA with respect to the number of available processor cores. initialized classes are assigned to either manually written
or default values. Heap snapshotting traverses all the objects
The results show that, for a reduced number of threads, such
reachable from static fields of the initialized classes and con-
as 1 or 4, the savings in analysis time can be even greater,
making RTA a good choice for constrained environments structs an object graph, i.e., a directed graph of instances
such as GitHub Actions [1] or similar CI pipelines. whose edges are references to other objects reachable via in-
Our implementation, which is based on the Native Image stance fields or array slots. The object graph constitutes the
component of GraalVM [13], is written in Java and Java is image heap and is stored as a part of the binary file. When
the application is started, the image heap is mapped directly
used for all examples in this paper. However, our approach is
into memory [26]. This process and its interaction with static
not limited to Java or languages that compile to Java bytecode.
It can be applied to all managed languages that are amenable analysis are discussed in more details in Section 3.5.
to points-to analysis, such as C# or other languages of the After the analysis finishes, the ahead-of-time compila-
.NET framework. tion is started. We use the Graal Compiler for compilation.
In summary, this paper contributes the following: Methods are represented using the Graal Intermediate Rep-
resentation (IR), which is graph-based and models both the
• We introduce a new variant of rapid type analysis for control-flow and the data-flow dependencies between nodes
the context of GraalVM Native Image. It supports class [21]. At this point, the IR graphs are optimized using the
initialization at build time and heap snapshotting. facts proven by the analysis. Finally, the image heap and
• We extend the proposed algorithm to be parallel and compiled code are written into the image file.
incremental. The incrementality is achieved by using
method summaries that sum up the effect of each ana-
2.1 Points-to Analysis in GraalVM Native Image
lyzed method. These summaries can be serialized and
reused between multiple builds. This section presents the points-to analysis used in GraalVM
• We provide a detailed comparison of the new variant Native Image, which was introduced in [26]. The analysis
of RTA with a points-to analysis for ahead-of-time is context-insensitive, path-sensitive, flow-insensitive for
fields but flow-sensitive for local variables. It starts with a
1 Note that the averages were computed using all our benchmarks including set of root methods and iteratively processes all transitively
those presented in the appendix only. reachable methods until a fixed-point is reached.
2
Comparing Rapid Type Analysis with Points-To Analysis in GraalVM Native Image MPLR, Oct 22–27, 2023, Cascais, Portugal
During the analysis, objects are represented by their types public class Hello {
only, not by their allocation sites as is common in other public static void main() {
pointer analyses [19]. Using the type abstraction is a suffi- new Hello().foo(new A());
ciently powerful approximation which yields good results in log();
practice when the goal is to compute reachable program ele- }
ments, while keeping the analysis time reasonably low. This static void log() { new B(); }
type information is enough to enable compiler optimizations void foo(I i) { i.bar(); }
such as virtual method de-virtualization. }
interface I { void bar(); }
Each reachable method is parsed from bytecode into the
class A implements I {...}
Graal IR, which is then transformed into a type-flow graph.
class B implements I {...}
Nodes of type-flow graphs include those representing in-
structions as well as nodes representing formal parameters
and return values of methods. The nodes are connected via Figure 1. Running example for analysis.
directed use edges.
Each node maintains a type-state information about all
types that can reach it. Allocation nodes, i.e., nodes repre-
senting allocation instructions, act as sources that produce • An allocation node an2 for A, connected to in1 as a
types, which are then propagated along the use edges (with source of argument types in the call of foo().
the input/output of nodes representing method invocations • An invocation node in2 for the call of log().
handled in a different way as discussed below). Once a type Since an1 is used by in1 as a source of its receiver types
is added to a type-state, it is never removed. Thus, the size and since the invocation is virtual, as soon as the type Hello
of all type states can only grow. In any compilation run, the appears in an1 , the resolution of virtual methods is used,
number of program elements (classes, methods, and fields) is and Hello.foo() is found as the method to be invoked. The
given and finite. As the number of reachable elements only body of Hello.foo() is parsed and transformed into the
grows during the analysis and there is a fixed upper-bound, corresponding type-flow graph with the following nodes:
termination is guaranteed. The worst-case scenario happens
• A formal-parameter node fn1 used as a source of types
when all program elements are reachable by the analysis.
of the implicit this parameter.
Type-flow graphs of methods are connected into a sin-
• A formal-parameter node fn2 used as a source of types
gle interprocedural graph covering the whole application.
of the formal parameter i.
For that, nodes producing arguments of method calls are
• An invocation node in3 for the call of bar() that uses
connected with formal-parameters nodes of the target meth-
fn2 as a source of its receiver types.
ods, and return nodes from the target methods are connected
back into the invocation nodes in the callers. The input edges Now, an1 and an2 get connected to fn1 and fn2 , resp., allowing
of invocation nodes are thus not used for regular type propa- a flow of types from Hello.main() into Hello.foo(). The
gation but rather for steering the interconnection of sources type A can hence flow from an2 to fn2 and be used as a
of arguments with the formal-parameter nodes (and of the receiver type of in3 that is constructed for the call I.bar(),
appropriate return node with the invocation node). upon which the resolution selects A.bar() as the call target.
For static methods, this linkage happens when the type- The call to Hello.log() is static, and so its target can be
flow of the caller is created. For virtual methods, the linkage determined directly. Its type flow-graph contains an alloca-
happens dynamically during the analysis. Every time a new tion node an3 of B. Note that while the type B is instantiated,
type is added into the type-state of a receiver of a method its method B.bar() is not considered reachable as an3 has
call, it is used to resolve, i.e., to identify, the concrete method no use edge, and so it can never flow out of the method and
to be linked. get into the invocation node of I.bar() in Hello.foo().
To better understand our PTA, let us now walk through an The results of the points-to analysis are useful not only
example. For brevity, we omit calls to constructors and excep- to identify reachable elements but also for many compiler
tion handling. Consider the program in Figure 1. The analysis optimizations. For example, they can be used to remove
starts with the entry point Hello.main(). The method is unnecessary casts, remove dead branches of instanceof
parsed, and the type-flow graph in Figure 2 created. It con- checks that are always true or false, exclude fields that are
tains the following nodes: never accessed, and to optimize virtual calls with a limited
number of receiver types. Knowing the set of receivers and
their types allows one to devirtualize method calls with only
• An invocation node in1 for the call of foo(). one receiver type, employ polymorphic inline caching [10]
• An allocation node an1 for Hello, connected to 𝑖𝑛 1 as when there are a few receiver types only, and perform more
a source of receiver types of the call of foo(). method inlining, which can lead to subsequent optimizations.
3
MPLR, Oct 22–27, 2023, Cascais, Portugal Kozak, Jovanovic, Stancu, Vojnar, and Wimmer.
and the later AOT compilation step. For example, distinguish- Table 1. Correspondence between bytecode instructions and
ing between read and written fields is not needed for RTA collections in method summaries.
itself, but AOT compilation requires the information since it
automatically removes never accessed fields [26]. The infor- Bytecode instruction Collection in the summary
mation about which fields are read is also needed to drive
the heap snapshotting (cf. Section 3.5). new Instantiated types
The internal state of the analysis can be viewed as con- anewarray Instantiated types
sisting of a worklist containing all methods that still need to multianewarray Instantiated types
be analyzed and the following pieces of information associ- getfield Read fields
ated with the representation kept by the compiler for types, putfield Written types
methods, and fields: invoke* Directly/Virtually called methods
mark elements are called before the register methods that Algorithm 2 RTA handling of virtual methods.
do the resolution. Such an ordering is not possible because 1: procedure registerAsVirtualInvoked(𝑚)
discovering instantiated types and invoked methods is inter- 2: if 𝑚𝑎𝑟𝑘 (𝑚.𝑖𝑠𝑉 𝑖𝑟𝑡𝑢𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑑) then
connected. Discovering new instantiated types makes new 3: 𝑡 ← 𝑚.𝑑𝑒𝑐𝑙𝑎𝑟𝑖𝑛𝑔𝑇𝑦𝑝𝑒
methods reachable from invokes within already analyzed 4: 𝑡 .𝑣𝑖𝑟𝑡𝑢𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑑𝑀𝑒𝑡ℎ𝑜𝑑𝑠.𝑎𝑑𝑑 (𝑚)
methods and vice versa. 5: for 𝑠𝑢𝑏𝑡 ∈ 𝑡 .𝑖𝑛𝑠𝑡𝑎𝑛𝑡𝑖𝑎𝑡𝑒𝑑𝑆𝑢𝑏𝑡𝑦𝑝𝑒𝑠 do
6: 𝑟𝑒𝑠𝑜𝑙𝑣𝑒𝑑 ← 𝑠𝑢𝑏𝑡 .𝑟𝑒𝑠𝑜𝑙𝑣𝑒𝑀𝑒𝑡ℎ𝑜𝑑 (𝑚)
Algorithm 1 Rapid type analysis worklist loop.
7: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑟𝑒𝑠𝑜𝑙𝑣𝑒𝑑)
Input: The set of root methods 8: end for
Output: All reachable types, methods, and fields 9: end if
1: 𝑤𝑜𝑟𝑘𝑙𝑖𝑠𝑡 ← 𝑟𝑜𝑜𝑡𝑀𝑒𝑡ℎ𝑜𝑑𝑠 10: end procedure
2: while 𝑤𝑜𝑟𝑘𝑙𝑖𝑠𝑡 ≠ ∅ do 11: procedure registerAsInstantiated(𝑡)
3: 𝑚𝑒𝑡ℎ𝑜𝑑 ← 𝑟𝑒𝑚𝑜𝑣𝑒𝐹𝑖𝑟𝑠𝑡 (𝑤𝑜𝑟𝑘𝑙𝑖𝑠𝑡) 12: if 𝑚𝑎𝑟𝑘 (𝑡 .𝑖𝑠𝐼𝑛𝑠𝑡𝑎𝑛𝑡𝑖𝑎𝑡𝑒𝑑) then
4: 𝑖𝑟𝐺𝑟𝑎𝑝ℎ ← 𝑝𝑎𝑟𝑠𝑒𝑀𝑒𝑡ℎ𝑜𝑑 (𝑚𝑒𝑡ℎ𝑜𝑑) 13: for 𝑠𝑢𝑝𝑒𝑟𝑡 ∈ 𝑡 .𝑠𝑢𝑝𝑒𝑟𝑇𝑦𝑝𝑒𝑠 do
5: 𝑠𝑢𝑚𝑚𝑎𝑟𝑦 ← 𝑒𝑥𝑡𝑟𝑎𝑐𝑡𝑆𝑢𝑚𝑚𝑎𝑟𝑦 (𝑖𝑟𝐺𝑟𝑎𝑝ℎ) 14: 𝑠𝑢𝑝𝑒𝑟𝑡 .𝑖𝑛𝑠𝑡𝑎𝑛𝑡𝑖𝑎𝑡𝑒𝑑𝑆𝑢𝑏𝑡𝑦𝑝𝑒𝑠.𝑎𝑑𝑑 (𝑡)
6: 𝑎𝑝𝑝𝑙𝑦𝑆𝑢𝑚𝑚𝑎𝑟𝑦 (𝑠𝑢𝑚𝑚𝑎𝑟𝑦) 15: end for
7: end while 16: for 𝑠𝑢𝑝𝑒𝑟𝑡 ∈ 𝑡 .𝑠𝑢𝑝𝑒𝑟𝑇𝑦𝑝𝑒𝑠 do
8: procedure applySummary(𝑠𝑢𝑚𝑚𝑎𝑟𝑦) 17: for 𝑚 ∈ 𝑠𝑢𝑝𝑒𝑟𝑡 .𝑣𝑖𝑟𝑡𝑢𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑑𝑀𝑒𝑡ℎ𝑜𝑑𝑠 do
9: for 𝑚 ← 𝑠𝑢𝑚𝑚𝑎𝑟𝑦.𝑑𝑖𝑟𝑒𝑐𝑡𝐼𝑛𝑣𝑜𝑘𝑒𝑠 do 18: 𝑟𝑒𝑠𝑜𝑙𝑣𝑒𝑑 ← 𝑡 .𝑟𝑒𝑠𝑜𝑙𝑣𝑒𝑀𝑒𝑡ℎ𝑜𝑑 (𝑚)
10: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑚) 19: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑟𝑒𝑠𝑜𝑙𝑣𝑒𝑑)
11: end for 20: end for
12: for 𝑚 ← 𝑠𝑢𝑚𝑚𝑎𝑟𝑦.𝑣𝑖𝑟𝑡𝑢𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑠 do 21: end for
13: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝑉 𝑖𝑟𝑡𝑢𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑚) 22: end if
14: end for 23: end procedure
15: for 𝑡 ← 𝑠𝑢𝑚𝑚𝑎𝑟𝑦.𝑖𝑛𝑠𝑡𝑎𝑛𝑡𝑖𝑎𝑡𝑒𝑑𝑇𝑦𝑝𝑒𝑠 do
16: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝐼𝑛𝑠𝑡𝑎𝑛𝑡𝑖𝑎𝑡𝑒𝑑 (𝑡)
17: end for described below, the method VirtualThread.sleep would
18: // ... 𝑠𝑖𝑚𝑖𝑙𝑎𝑟 𝑙𝑜𝑜𝑝𝑠 𝑓 𝑜𝑟 𝑜𝑡ℎ𝑒𝑟 𝑝𝑎𝑟𝑡𝑠 ... be considered invoked even when the support for virtual
19: end procedure threads was disabled.
20: procedure registerAsInvoked(𝑚)
21: if 𝑚𝑎𝑟𝑘 (𝑚.𝑖𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑) then public class Thread {
22: 𝑤𝑜𝑟𝑘𝑙𝑖𝑠𝑡 .𝑎𝑑𝑑 (𝑚) public static void sleep(long millis) {
23: end if ...
24: end procedure if (currentThread() instanceof VirtualThread
25: procedure mark(𝑓 𝑙𝑎𝑔) vthread){
vthread.sleep(millis);
26: return 𝑓 𝑙𝑎𝑔.𝑐𝑜𝑚𝑝𝑎𝑟𝑒𝐴𝑛𝑑𝑆𝑒𝑡 (𝑓 𝑎𝑙𝑠𝑒, 𝑡𝑟𝑢𝑒)
return;
27: end procedure
}
...
}
3.3 Invoke Special Handling }
Another interesting case that we would like to discuss is in-
vokespecial. Differentiating static invokes and special in-
Figure 3. Invoke special example.
vokes is not necessary for correctness. However, it increases
precision because there are cases when a special invoke is
reachable, but no object is instantiated on which such method Due to the above, we handle invokespecial separately as
can be called. This happens, for example, when analyzing the shown in Algorithm 3. The method registerAsSpecialIn-
method java.lang.Thread.sleep. A simplified version of voked (lines 1–10) performs two tasks: First, it adds the called
the method for Java 20 can be found in Figure 3. It contains method to the set of invoked special methods on the declar-
the handling of virtual threads from the project Loom [4] ing type (line 4). Then, it calls registerAsInvoked but only
(instances of VirtualThread) although their usage has to if any subtype of the declaring type has been instantiated so
be explicitly enabled by a command line option. Otherwise, far (lines 6–8). Similarly to the previously described handling
no virtual thread is ever created, and the content of the if of virtual methods, it is also necessary to handle the case
block is dead code. However, without the special handling where the method is processed first and the type instantiated
6
Comparing Rapid Type Analysis with Points-To Analysis in GraalVM Native Image MPLR, Oct 22–27, 2023, Cascais, Portugal
later. Therefore, extend the method registerAsInstanti- Table 2. Method summaries for the running example.
ated with another loop that iterates over all invoked special
methods of all supertypes of the newly instantiated type Method Method summary
and processes them via registerAsInvoked (lines 16–18). Instantiated types Method invokes
This way, we delay the processing of invokespecial only Direct Virtual
after a suitable type upon which they can be called has been
instantiated. main Hello, A log foo
log B
Algorithm 3 RTA handling of invoke special. foo I.bar
1: procedure registerAsSpecialInvoked(𝑚)
2: if 𝑚𝑎𝑟𝑘 (𝑚.𝑖𝑠𝑆𝑝𝑒𝑐𝑖𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑑) then Table 3. Results of the analyses on the running example.
3: 𝑡 ← 𝑚.𝑑𝑒𝑐𝑙𝑎𝑟𝑖𝑛𝑔𝑇𝑦𝑝𝑒
4: 𝑡 .𝑠𝑝𝑒𝑐𝑖𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑑𝑀𝑒𝑡ℎ𝑜𝑑𝑠.𝑎𝑑𝑑 (𝑚)
5: if 𝑡 .𝑖𝑛𝑠𝑡𝑎𝑛𝑡𝑖𝑎𝑡𝑒𝑑𝑆𝑢𝑏𝑡𝑦𝑝𝑒𝑠.𝑛𝑜𝑡𝐸𝑚𝑝𝑡𝑦 () then Analysis Results
6: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑚) Instantiated types Invoked methods
7: end if PTA Hello, A, B log, foo, A.bar
8: end if RTA Hello, A, B log, foo, {A,B}.bar
9: end procedure
10: procedure registerAsInstantiated(𝑡)
11: ...
method is also marked as invoked and added to the worklist.
12: for 𝑠𝑢𝑝𝑒𝑟𝑡 ∈ 𝑡 .𝑠𝑢𝑝𝑒𝑟𝑇𝑦𝑝𝑒𝑠 do
When the analysis of Hello.main finishes, there are two
13: ...
more methods to process, Hello.foo and Hello.log.
14: for 𝑚 ∈ 𝑠𝑢𝑝𝑒𝑟𝑡 .𝑠𝑝𝑒𝑐𝑖𝑎𝑙𝐼𝑛𝑣𝑜𝑘𝑒𝑑𝑀𝑒𝑡ℎ𝑜𝑑𝑠 do
Hello.foo virtually calls the method I.bar. All instan-
15: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑚)
tiated subtypes of I are considered as receivers. Currently,
16: end for
the only instantiated subtype of I is A. Consequently, only
17: end for
A.bar is marked as invoked and added into the worklist.
18: end procedure
The analysis of Hello.log seems straightforward as it
only marks the type B as instantiated. However, when travers-
ing the supertypes of B in registerAsInstantiated, the
3.4 Running Example interface I is considered as well, whose virtually invoked
To demonstrate the idea of RTA with method summaries, method I.bar is resolved against B. This resolution identi-
consider again the program in Figure 1. Summaries for all its fies B.bar as a call target, which is then marked as invoked
methods can be found in Table 2. Note that the empty sets and added into the worklist. This is an example of a loss of
inside the summaries are omitted for brevity. We stress that precision compared to the points-to analysis, which would
the summaries presented in the table are in fact created lazily correctly determine A.bar as the only call target. The results
when their corresponding methods are marked as invoked. of running both analyses on the example are presented in
The method Hello.main is the entry point, therefore it is Table 3. The method B.bar, which is included among reach-
used to initialize the worklist and consequently processed able methods due to the imprecision of RTA, is highlighted
first. Its bytecode is parsed and its summary is created. As in red.
shown in the summary, it instantiates the types Hello and A,
has a virtual invoke of the method Hello.foo, and a direct 3.5 Heap Snapshotting and Embedded Constants
invoke of the method Hello.log. Application initialization at build time enables a significantly
The summary for Hello.main is now applied to update faster application startup, but it poses a challenge for the anal-
the state of the analysis. First, the types Hello and A are ysis. The initialization is executed already during analysis,
marked as instantiated. None of these types or their super- when a given class is marked as reachable. The initialization
types have any methods marked as virtually invoked and no code can create arbitrary objects and use them to initialize
new call targets are discovered. When processing the virtual static fields. The object graphs reachable from these fields
method call Hello.foo, the set of all instantiated subtypes of then have to be traversed because they can contain types
Hello is considered, which currently has only one element, not seen in the analyzed methods.
Hello itself. The call is then resolved against the type Hello, The object graphs are traversed concurrently with the
which resolves to Hello.foo as the call target. Consequently, analysis by a component called the heap scanner. The scan-
Hello.foo is marked as invoked and added into the work- ner works in tandem with the analysis and only processes
list. The invoke of Hello.log is direct, so the corresponding the values of fields that are marked as read. Processing other
7
MPLR, Oct 22–27, 2023, Cascais, Portugal Kozak, Jovanovic, Stancu, Vojnar, and Wimmer.
fields is not necessary because if the analysis does not dis- task list (see Algorithm 4). Before the analysis is started, a
cover any instruction that reads from a field, then its value thread pool is created, which executes all scheduled tasks.
can never be read at runtime. The scanner is notified by the Every root method is passed immediately into registerAs-
analysis for every read field, and, if not already done, it in- Invoked (line 2), which was updated in the following manner.
cludes its content into the image heap, and it also processes If the method mark returns true, the execution of onInvoked
all objects transitively reachable from the field’s value by is scheduled as a separate task (line 7) so that any available
following its fields that are already marked as read. If the thread in the thread pool can execute it. The method onIn-
heap scanner discovers a so-far unseen type, it notifies the voked obtains the summary for each invoked method and
analysis to treat it as instantiated [26]. applies it to update the state of the analysis.
The values from static final fields of initialized classes The methods registerAsVirtualInvoked and regis-
can be constant folded into the compiled methods during terAsInstantiated of Algorithm 2 do not need to be up-
bytecode parsing. We call such values embedded constants. dated, both of them call registerAsInvoked, which is al-
Every time such a constant is discovered, it is given as a root ready updated to be parallel. The mark method of Algorithm 1
to the heap scanner. is already using an atomic operation to ensure that only one
To better explain the concept of constant folding of initial- thread processes a newly reachable element even if multiple
ized static final fields, take a look at the example in Figure 4. threads attempt to mark it concurrently.
Assume that the class EmbeddedConstantsExample is initial- Note that Algorithm 2 is already carefully designed to be
ized at build time, i.e., that the static initializer is executed safe with regards to parallel execution. In registerAsVir-
during analysis. The method selectComponent selects some tualInvoked, the method must be added to virtualIn-
component based on arbitrary application logic. The result- vokedMethods (line 4) before iterating the instantiated sub-
ing object is used to initialize the field c. The method main types (lines 6–9). Likewise, in registerAsInstantiated,
is the entry point. When the analysis of main starts and its the type must be added to all instantiatedSubtypes sets
bytecode is parsed, the compiler notices that the field access (lines 15–17) before iterating the virtualInvokedMethods
of c can be constant folded because it was intialized and (lines 19–24). This guarantees that a concurrent execution of
assigned a value that never changes (the field is declared fi- instantiatedSubtypes and virtualInvokedMethods that
nal). Therefore the constant c is embedded into the compiler affects the same virtual method does not miss to mark any
IR and then put into the method summary. resolved methods. Indeed, regardless of whether the virtual
method is first marked as invoked or the type is first marked
public class EmbeddedConstantsExample { as instantiated, the method is registered as invoked either
private static final Component c; by the loop on line 6 in registerAsVirtualInvoked or the
static { c = selectComponent(); } loop on line 20 in registerAsInstantiated.
private static Component selectComponent() {...}
public static void main() { c.execute(); } Algorithm 4 Excerpts of the parallel analysis.
}
1: for 𝑚 ∈ 𝑟𝑜𝑜𝑡𝑀𝑒𝑡ℎ𝑜𝑑𝑠 do
2: 𝑟𝑒𝑔𝑖𝑠𝑡𝑒𝑟𝐴𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑚)
Figure 4. Embedded constants example. 3: end for
4: procedure registerAsInvoked(𝑚)
5: if 𝑚𝑎𝑟𝑘 (𝑚.𝑖𝑠𝐼𝑛𝑣𝑜𝑘𝑒𝑑) then
Assume that the method selectComponent is the only
6: 𝑠𝑐ℎ𝑒𝑑𝑢𝑙𝑒 (() → 𝑜𝑛𝐼𝑛𝑣𝑜𝑘𝑒𝑑 (𝑚))
place where the class Component is instantiated and this
7: end if
method is only reachable from the class initializer of Em-
8: end procedure
beddedConstantsExample. Without taking the embedded
9: procedure onInvoked(𝑚)
constant into consideration, the class Component would not
10: 𝑖𝑟𝐺𝑟𝑎𝑝ℎ ← 𝑝𝑎𝑟𝑠𝑒𝑀𝑒𝑡ℎ𝑜𝑑 (𝑚)
be considered as instantiated when processing the virtual
11: 𝑠 ← 𝑒𝑥𝑡𝑟𝑎𝑐𝑡𝑆𝑢𝑚𝑚𝑎𝑟𝑦 (𝑖𝑟𝐺𝑟𝑎𝑝ℎ)
call of Component.execute and then its execute method
12: 𝑎𝑝𝑝𝑙𝑦𝑆𝑢𝑚𝑚𝑎𝑟𝑦 (𝑠)
would not be considered as a call target, even though it is
13: end procedure
actually executed at run time. To handle this problem, the
type of the embedded constant c and the types of any other
objects transitively reachable from the constant by following
fields marked as read are treated as instantiated. 3.7 Incremental Analysis
Method summaries are designed so that they can be easily
3.6 Parallel Analysis serialized and reused. Each method summary can be trans-
Algorithm 1 presented above is single-threaded. To enable formed into a purely textual SerializedSummary. Classes,
parallelism, we replace the explicit worklist with a parallel methods, and fields are represented as follows:
8
Comparing Rapid Type Analysis with Points-To Analysis in GraalVM Native Image MPLR, Oct 22–27, 2023, Cascais, Portugal
• Each class is represented by a ClassId, which consists However, the summaries could also be fetched from a remote
of the full name of the class. source or included with the libraries the compiled application
• Each method is represented by a MethodId consisting is using, so that even the first execution in a given context
of the ClassId of the declaring class, the method name, (user account, host, etc.) can benefit from incrementality.
and the signature to differentiate overloaded methods. Since the method could have changed in between the
• Each field is represented by a FieldId consisting of builds, it is important to check validity of the summary
the ClassId of the declaring class and the field name. (line 3). That can be achieved by storing the hash of the
The process of serializing summaries is straightforward bytecode instructions along with the summary. Smarter ap-
because it only requires to pick specific string identifiers proaches could take into consideration timestamps on the jar
based on the rules above. On the other hand, the resolu- files or library version numbers, but since our goal was to es-
tion, which transforms the SerializedSummary back into timate the benefit that can be obtained by reusing summaries,
the MethodSummary, is more complex. we decided to use only hashing for the initial prototype.
Resolving ClassIds back into classes is done by looking If the SerializedSummary is available and is still valid, it
them up using a specialized Classloader, which is a special is resolved back into a MethodSummary based on the rules
class responsible for loading classes [11]. Resolving methods described above (line 4). If the resolution is successful, the
and fields is a two-step process. First, the declaring class summary can be reused, otherwise it is necessary to extract
is resolved. If the class resolution is successful, the algo- a new one by parsing the bytecode (lines 9–11).
rithm locates the requested field/method by iterating over
all declared methods/fields. We aim to improve the lookup Algorithm 5 Retrieving a method summary.
procedure in the future—the naive iteration is a limitation of
1: procedure getSummary(𝑚𝑒𝑡ℎ𝑜𝑑)
the current implementation only.
2: 𝑠𝑒𝑟𝑖𝑎𝑙𝑖𝑧𝑒𝑑 ← 𝑙𝑜𝑎𝑑𝑆𝑢𝑚𝑚𝑎𝑟𝑦 (𝑚𝑒𝑡ℎ𝑜𝑑)
Unfortunately, not all summaries can be reused. For a sum-
3: if 𝑠𝑒𝑟𝑖𝑎𝑙𝑖𝑧𝑒𝑑 ≠ 𝑛𝑢𝑙𝑙 𝑎𝑛𝑑 𝑖𝑠𝑉 𝑎𝑙𝑖𝑑 (𝑠𝑒𝑟𝑖𝑎𝑙𝑖𝑧𝑒𝑑) then
mary to be reusable, it has to match the following criteria:
4: 𝑠𝑢𝑚𝑚𝑎𝑟𝑦 ← 𝑟𝑒𝑠𝑜𝑙𝑣𝑒 (𝑠𝑒𝑟𝑖𝑎𝑙𝑖𝑧𝑒𝑑)
• Each identifier has to be stable. We call an identifier 5: if 𝑠𝑢𝑚𝑚𝑎𝑟𝑦 ≠ 𝑛𝑢𝑙𝑙 then
stable if its resolution in different analysis runs always 6: return 𝑠𝑢𝑚𝑚𝑎𝑟𝑦
results in the same element. Unfortunately, lambda 7: end if
names, proxy names and in general names of all gen- 8: end if
erated classes and methods are potentially unstable. 9: 𝑖𝑟𝐺𝑟𝑎𝑝ℎ ← 𝑝𝑎𝑟𝑠𝑒𝑀𝑒𝑡ℎ𝑜𝑑 (𝑚𝑒𝑡ℎ𝑜𝑑)
• All embedded constants have to be trivial. We call 10: 𝑠𝑢𝑚𝑚𝑎𝑟𝑦 ← 𝑒𝑥𝑡𝑟𝑎𝑐𝑡𝑆𝑢𝑚𝑚𝑎𝑟𝑦 (𝑖𝑟𝐺𝑟𝑎𝑝ℎ)
a constant trivial if it is a primitive data type or an 11: return 𝑠𝑢𝑚𝑚𝑎𝑟𝑦
immutable type with a fixed internal structure, such 12: end procedure
as java.lang.String. If a given class is immutable
and has a fixed internal structure, the set of types in
its object graph is identical for all instances. There- Reusing summaries from previous builds allows the anal-
fore, it is enough to process only a single instance. For ysis to skip the overhead of parsing. Unfortunately, parsing
commonly used types such as java.lang.String, it still has to occur for the compilation that follows, so until
is guaranteed that at least one such instance is pro- the compilation pipeline is incremental as well, the benefits
cessed when traversing the image heap, and so these can be seen only on the analysis time, not the whole build.
embedded constants can be ignored in summaries.
Note, however, that both of these limitations are merely 4 Evaluation
implementation-specific. They are not inherent to the pro- This section compares our implementation of RTA and PTA
posed algorithm and could be lifted in the future. Imple- in the context of GraalVM Native Image. We use Oracle
menting a proper handling for these two cases would be a GraalVM 23.0 based on JDK 20.
significant engineering effort with little added value research- The experiments are executed on a dual-socket Intel Xeon
wise—hence we decided to keep these restrictions for now. E5-2630 v3 running at 2.40 GHz with 8 physical/16 logical
To integrate the reuse of summaries into the previous al- cores per socket, 128 GiB main memory, running Oracle
gorithms, the process of parsing the bytecode and extracting Linux Server release 7.3. The benchmark execution is pinned
summaries is moved to a new procedure getSummmary de- to one of the two CPUs, and TurboBoost was disabled to
scribed in Algorithm 5. The procedure first tries to load a avoid instability. The number of threads is by default set
serialized summary for the given method (line 2). At the mo- to 16 with the exception of scalability experiments where it
ment, all serialized summaries preserved from previous com- is a part of the configuration. Each benchmark is executed
pilations are stored in a file, which is loaded into a map as- 10 times, and the average values are presented. We do not
sociating MethodIds to corresponding serialized summaries. include the deviation as it is significantly smaller than the
9
MPLR, Oct 22–27, 2023, Cascais, Portugal Kozak, Jovanovic, Stancu, Vojnar, and Wimmer.
differences between PTA and RTA in most cases. We use the and fields. Using metrics such as lines of code or the num-
following applications for the evaluation: ber of classes could be misleading because only reachable
• Helloworld: A simple Java application printing a text elements are analyzed and compiled. Since these metrics are
to the standard output. Even such a simple application interconnected and follow the same pattern, we decided to
actually consists of more than 1,000 classes and 10,000 present the number of reachable methods as the main metric.
methods, e.g., for the necessary charset conversion This number directly influences not only the scope of the
code and the runtime system. analysis (how many methods need to be processed) but also
• DaCapo: A benchmark suite that consists of client- the workload of the compilation phase afterwards. Details
side Java benchmarks, trying to exercise the complex about types and fields can be found in the appendix.
interactions between the architecture, compiler, virtual We can immediately observe that the imprecision of RTA
machine and running application [7]. We use a subset increases the number of reachable methods for all bench-
of the benchmark suite because some benchmarks are marks, as was expected. However, an interesting trend can be
not compatible with our AOT compilation. observed. Whereas there is a significant difference between
• Renaissance: A benchmark suite that consists of real- reachable elements for HelloWorld and the other smaller
world, concurrent, and object-oriented workloads that Renaissance and Dacapo benchmarks, the difference gets
exercise various concurrency primitives of the JVM usually significantly smaller for the bigger applications. Nev-
[15]. We use a subset of the benchmark suite because ertheless, one cannot say that the difference is uniformly
some benchmarks are not compatible with our AOT decreasing with the increasing size of the applications. In-
compilation. deed, for example, the number of reachable methods for
• {Spring, Micronaut, Quarkus} Helloworld: Simple hel- Quarkus Tika is increased by 6 %, while a much bigger Re-
loworld applications in the corresponding frameworks. naissance chi-square is increased by 8 %. This suggests
• Quarkus Registry [16]: A large real-world application that not only the size of the compiled application but also
using the Quarkus framework. It is used to host the its structure influence the performance and precision.
Quarkus extension registry.
• Micronaut MuShop [14]: A large demo application us-
4.2 Analysis Time
ing the Micronaut framework. We use three services:
Order, Payment, and User. The time that is reported by GraalVM Native Image as the
• Spring Petclinic 2 : A popular demo application of the analysis time includes the time spent running application
Spring framework [25]. initialization code. We treat this step as a constant factor
• Micronaut Shopcart: A demo application of the Micro- that cannot be directly improved by different analysis meth-
naut framework [12] performing similar tasks to the ods. In order to measure the influence on analysis more
Petclinic but in a different domain. precisely, we subtracted it from the overall analysis time. It
• Quarkus Tika3 : An extension to the Quarkus Frame- can be seen that RTA outperforms PTA on all benchmarks
work [17] that provides functionality to parse docu- apart from the small ones. The most notable savings are
ments using the Apache Tika library4 . for Spring Petclinic, for which the analysis time is re-
duced by 64 %. The biggest Renaissance benchmarks log-
The results are presented in Table 4. The number of reach- regression, and dec-tree also exhibit a significant anal-
able methods has been divided by 1,000 and similar conver- ysis time reduction. Unfortunately, these benchmarks are
sions were performed to present values in seconds and MB. not fully supported by GraalVM Native Image and currently
The values were rounded and then compared. For Dacapo fail during compilation. We have decided to include at least
and Renaissance, the table presents a subset of the bench- the analysis time of these benchmarks because they are the
marks only (a few small, a few mid-sized and a few of the biggest of our suite in terms of reachable methods.
biggest). The data for all benchmarks can be found in the ap-
pendix5 . We highlight Spring Petclinic in violet as we discuss
its results often. 4.3 Build Time
Since the reduced precision of RTA puts more workload on
4.1 Reachable Elements
the compilation phase that follows, we also measured the
In order to get an insight into the actual size of our bench- whole build time. It can be seen that while the imprecision of
marks, we measured the number of reachable types, methods, RTA indeed negatively influences small applications such as
HelloWorld or smaller benchmarks from the Renaissance
2 https://github.com/spring-projects/spring-petclinic
3 https://github.com/quarkiverse/quarkus-tika
and Dacapo bench suites, for bigger applications the time
4 https://tika.apache.org/ saved in the analysis outweighs the extra compilation time.
5 Incase of acceptance, the paper including the appendix will be submitted The biggest savings were again obtained for Spring Pet-
to https://arxiv.org/ so that the data are easily accessible. clinic where the overall build was reduced by 35 %.
10
Comparing Rapid Type Analysis with Points-To Analysis in GraalVM Native Image MPLR, Oct 22–27, 2023, Cascais, Portugal
Reachable Methods Analysis Time [s] Total time [s] Binary size [MB]
Suite Benchmark PTA RTA PTA RTA PTA RTA PTA RTA
Console helloworld 18 +17% 14 +21% 36 +17% 13 +23%
avrora 24 +25% 12 -8% 51 +6% 23 +30%
fop 94 +4% 46 -30% 128 -10% 105 +11%
Dacapo
jython 71 +8% 55 -35% 140 -26% 134 +9%
luindex 26 +23% 13 -8% 54 +7% 32 +25%
micronaut-helloworld-wrk 74 +4% 34 -32% 88 -9% 45 +18%
mushop:order 168 +2% 102 -59% 209 -30% 104 +13%
mushop:payment 82 +4% 36 -33% 91 -10% 50 +14%
mushop:user 115 +3% 57 -44% 135 -18% 76 +13%
Microservices petclinic-wrk 207 +4% 159 -64% 297 -35% 144 +15%
quarkus-helloworld-wrk 52 +6% 18 -22% 69 -3% 50 +4%
quarkus:registry 111 +5% 49 -39% 126 -16% 69 +19%
spring-helloworld-wrk 67 +4% 30 -33% 87 -10% 47 +13%
tika-wrk 82 +6% 29 -28% 117 -6% 88 +6%
chi-square 173 +8% 129 -60% 260 -30% 100 +17%
dec-tree 324 +6% 2009 -95% X X X X
future-genetic 27 +22% 15 0% 44 +5% 19 +21%
gauss-mix 189 +8% 146 -61% 286 -32% 107 +17%
Renaissance
log-regression 334 +7% 2215 -95% X X X X
page-rank 171 +8% 129 -60% 258 -31% 119 +13%
reactors 30 +13% 19 +16% 47 +11% 19 +21%
scala-stm-bench7 30 +20% 19 +26% 49 +14% 19 +21%
Mi:hello PTA 1 PTA 4 PTA 8 PTA 16 Mi:hello RTA 1 RTA 4 RTA 8 RTA 16
Mu:order Mu:order
Mu:payment Mu:payment
Mu:user Mu:user
Q:hello Q:hello
Q:registry Q:registry
Q:Tika Q:Tika
S:hello S:hello
S:petclinic S:petclinic
R:chi-square R:chi-square
R:dec-tree R:dec-tree
R:gauss-mix R:gauss-mix
R:log-regression R:log-regression
R:page-rank R:page-rank
R:reactors R:reactors
R:scala-stm-bench R:scala-stm-bench
20
40
60
80
20
40
60
80
0
0
0
0
0
00
00
00
00
00
00
10
20
40
60
80
10
20
40
60
80
10
20
40
10
20
40
Analysis Time [s] Analysis Time [s]
Görel Hedin (Ed.). Springer Berlin Heidelberg, Berlin, Heidelberg, 126– Initialize Once, Start Fast: Application Initialization at Build Time.
137. Proc. ACM Program. Lang. 3, OOPSLA, Article 184 (oct 2019), 29 pages.
[19] Yannis Smaragdakis and George Balatsouras. 2015. Pointer Analysis. https://doi.org/10.1145/3360610
Foundations and Trends® in Programming Languages 2, 1 (2015), 1–69.
https://doi.org/10.1561/2500000014
[20] Manu Sridharan, Satish Chandra, Julian Dolby, Stephen J. Fink, and
A Detailed Results
Eran Yahav. 2013. Alias Analysis for Object-Oriented Programs. Springer Results for all our benchmarks are presented in Table 5.
Berlin Heidelberg, Berlin, Heidelberg, 196–232. https://doi.org/10.
1007/978-3-642-36946-9_8 B Detailed Reachable Program Elements
[21] Lukas Stadler, Thomas Würthinger, Doug Simon, Christian Wimmer,
and Hanspeter Mössenböck. 2013. Graal IR : An Extensible Declarative Reachable program elements (types, methods, and fields)
Intermediate Representation. computed by points-to analysis and our RTA for the bench-
[22] Vijay Sundaresan, Laurie Hendren, Chrislain Razafimahefa, Raja marks presented in Section 4 are presented in detail in Ta-
Vallée-Rai, Patrick Lam, Etienne Gagnon, and Charles Godin. 2000. ble 6. As with the previous tables, the number of reachable
Practical Virtual Method Call Resolution for Java. In Proceedings of
the 15th ACM SIGPLAN Conference on Object-Oriented Programming,
program elements has been divided by 1,000 and rounded
Systems, Languages, and Applications (Minneapolis, Minnesota, USA) before comparison. By inspecting the table, it can be seen
(OOPSLA ’00). Association for Computing Machinery, New York, NY, that these three metrics are correlated. Benchmarks with
USA, 264–280. https://doi.org/10.1145/353171.353189 more reachable methods have also more reachable types and
[23] Frank Tip and Jens Palsberg. 2000. Scalable Propagation-Based Call fields and vice versa.
Graph Construction Algorithms. In Proceedings of the 15th ACM SIG-
PLAN Conference on Object-Oriented Programming, Systems, Languages,
and Applications (Minneapolis, Minnesota, USA) (OOPSLA ’00). As- C Detailed Scalability Results
sociation for Computing Machinery, New York, NY, USA, 281–293. Detailed scalability results are presented in Figure 6 and
https://doi.org/10.1145/353171.353190 Figure 7.
[24] Ben L. Titzer. 2006. Virgil: Objects on the Head of a Pin. SIGPLAN Not.
41, 10 (oct 2006), 191–208. https://doi.org/10.1145/1167515.1167489 Received 20 February 2007; revised 12 March 2009; accepted 5 June
[25] VMWare. 2023. Spring. https://spring.io/
2009
[26] Christian Wimmer, Codrut Stancu, Peter Hofer, Vojin Jovanovic, Paul
Wögerer, Peter B. Kessler, Oleg Pliss, and Thomas Würthinger. 2019.
14
Comparing Rapid Type Analysis with Points-To Analysis in GraalVM Native Image MPLR, Oct 22–27, 2023, Cascais, Portugal
Reachable Methods Analysis Time [s] Total time [s] Binary size [MB]
Suite Benchmark PTA RTA PTA RTA PTA RTA PTA RTA
Console helloworld 18 +17% 14 +21% 36 +17% 13 +23%
avrora 24 +25% 12 -8% 51 +6% 23 +30%
batik 52 +12% 25 -24% 80 -2% 61 +18%
fop 94 +4% 46 -30% 128 -10% 105 +11%
h2 40 +8% 20 -25% 69 -4% 52 +10%
jython 71 +8% 55 -35% 140 -26% 134 +9%
Dacapo
luindex 26 +23% 13 -8% 54 +7% 32 +25%
lusearch 24 +25% 12 -8% 49 +6% 29 +28%
pmd 62 +5% 29 -31% 90 -7% 72 +10%
sunflow 52 +12% 27 -26% 88 -2% 46 +24%
xalan 48 +6% 24 -29% 76 -5% 48 +15%
micronaut-helloworld-wrk 74 +4% 34 -32% 88 -9% 45 +18%
mushop:order 168 +2% 102 -59% 209 -30% 104 +13%
mushop:payment 82 +4% 36 -33% 91 -10% 50 +14%
mushop:user 115 +3% 57 -44% 135 -18% 76 +13%
Microservices petclinic-wrk 207 +4% 159 -64% 297 -35% 144 +15%
quarkus-helloworld-wrk 52 +6% 18 -22% 69 -3% 50 +4%
quarkus:registry 111 +5% 49 -39% 126 -16% 69 +19%
spring-helloworld-wrk 67 +4% 30 -33% 87 -10% 47 +13%
tika-wrk 82 +6% 29 -28% 117 -6% 88 +6%
akka-uct 35 +17% 17 0% 49 +2% 22 +23%
als 317 +6% 1558 -94% X X X X
chi-square 173 +8% 129 -60% 260 -30% 100 +17%
dec-tree 324 +6% 2009 -95% X X X X
dotty 83 +10% 37 -16% 98 -4% 51 +16%
finagle-chirper 89 +8% 45 -29% 105 -12% 51 +16%
finagle-http 88 +7% 48 -33% 111 -14% 50 +18%
fj-kmeans 26 +23% 11 0% 35 +6% 18 +22%
future-genetic 27 +22% 15 0% 44 +5% 19 +21%
gauss-mix 189 +8% 146 -61% 286 -32% 107 +17%
log-regression 334 +7% 2215 -95% X X X X
Renaissance mnemonics 26 +23% 15 0% 43 +2% 18 +22%
movie-lens 177 +8% 109 -59% 222 -29% 106 +15%
naive-bayes 320 +6% 1277 -92% X X X X
page-rank 171 +8% 129 -60% 258 -31% 119 +13%
par-mnemonics 27 +19% 15 0% 43 +5% 19 +16%
philosophers 28 +18% 19 +16% 48 +10% 19 +16%
reactors 30 +13% 19 +16% 47 +11% 19 +21%
rx-scrabble 27 +22% 14 0% 42 +2% 27 +15%
scala-doku 27 +22% 11 0% 35 +6% 19 +21%
scala-kmeans 26 +23% 14 0% 41 +2% 18 +22%
scala-stm-bench7 30 +20% 19 +26% 49 +14% 19 +21%
scrabble 27 +19% 14 0% 41 +5% 26 +19%
15
MPLR, Oct 22–27, 2023, Cascais, Portugal Kozak, Jovanovic, Stancu, Vojnar, and Wimmer.
16
Analysis Time [s] Analysis Time [s]
C C
1000
2000
4000
1000
2000
4000
100
200
400
600
800
100
200
400
600
800
20
40
60
80
20
40
60
80
on on
so so
le le
D :h D :h
ac el ac el
ap lo ap lo
o: o:
av av
ro ro
D ra D ra
ac ac
ap ap
D o D o
ac :fo ac :fo
ap p ap p
o: o:
D jy D j y
ac th ac th
ap on ap on
o: o:
D lu D lu
ac in ac in
ap de ap de
o: x o: x
lu lu
se se
ar ar
ch ch
M M
i:h i:h
el el
lo lo
M M
u: u:
or or
M de M de
u:
pa r u:
pa r
PTA 1
ym ym
en en
t t
RTA 1
M M
u: u:
us us
er er
PTA 4
Q Q
:h :h
17
RTA 4
el el
Q lo Q lo
:r :r
Comparing Rapid Type Analysis with Points-To Analysis in GraalVM Native Image
eg eg
is is
PTA 8
tr tr
y y
RTA 8
Q Q
:T :T
ik ik
a a
S: S:
PTA 16
he he
S: llo S: llo
p
RTA 16
et pe
cl tc
R in R lin
:c ic :c ic
h hi
Figure 6. Scalability PTA results (log scale).