-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathAdaptiveOptimizationSystem.html
More file actions
1306 lines (1264 loc) · 61.9 KB
/
Copy pathAdaptiveOptimizationSystem.html
File metadata and controls
1306 lines (1264 loc) · 61.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
layout: default
---
<?xml version="1.0" encoding="utf8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd-->
<html xmlns="http://www.w3.org/1999/xhtml"
>
<head><title>12 Adaptive Optimization System</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<meta name="generator" content="TeX4ht (https://tug.org/tex4ht/)" />
<meta name="originator" content="TeX4ht (https://tug.org/tex4ht/)" />
<!-- xhtml,charset=utf8,2,html -->
<meta name="src" content="index.tex" />
<link rel="stylesheet" type="text/css" href="index.css" />
</head><body
>
<!--l. 2--><div class="crosslinks"><p class="noindent"></p></div>
<h2 class="chapterHead"><span class="titlemark">Chapter 12</span><br /><a
id="x15-14900012"></a>Adaptive Optimization System</h2>
<!--l. 5--><p class="noindent" >A comprehensive discussion of the design and implementation of the original Jikes
RVM adaptive optimization system is given in the OOPSLA 2000 paper by Arnold,
Fink, Grove, Hind and Sweeney. A number of aspects of the system have been
changed since 2000, so a better resource is a technical report <a
href="http://domino.research.ibm.com/library/cyberdig.nsf/1e4115aea78b6e7c85256b360066f0d4/30c2b5bb5352443885256f550066b5c1%21OpenDocument" >Nov. 2004 technical
report</a> that describes the architecture and implementation in some detail.
This section of the userguide is based on section 5 of the 2004 technical
report.
</p><!--l. 7--><p class="noindent" >The implementation of the Jikes RVM adaptive optimization system uses a number
of Java threads: several organizer threads in the runtime measurements component,
the controller thread, and the compilation thread. The various threads are loosely
coupled, communicating with each other through shared queues and/or the
other in memory data structures. All queues in the system are blocking
priority queues; if a consumer thread performs a dequeue operation when the
queue is empty, it suspends until a producer thread performs an enqueue
operation.
</p><!--l. 9--><p class="noindent" >The adaptive optimization system performs two primary tasks: selective optimization
and profile-directed inlining.
</p>
<!--l. 11--><p class="noindent" ><span class="paragraphHead"><a
id="x15-15000012"></a><span
class="cmbx-10">Selective Optimization</span></span>
The goal of selective optimization is to identify regions of code in which the
application spends significant execution time (often called “hot spots”), determine if
overall application performance is likely to be improved by further optimizing one or
more hot spots, and if so to invoke the optimizing compiler and install the resulting
optimized code in the virtual machine.
</p><!--l. 15--><p class="noindent" >In Jikes RVM, the unit of optimization is a method. Thus, to perform selective
optimization, first the runtime measurements component must identify candidate
methods (“hot methods”) for the controller to consider. To this end, it installs a
listener that periodically samples the currently executing method at every taken
yieldpoint. When it is time to take a sample, the listener inspects the thread’s call
stack and records a single compiled method id into a buffer. If the yieldpoint occurs
in the prologue of a method, then the listener additionally records the compiled
method id of the current activation’s caller. If the taken yieldpoint occurs on a loop
backedge or method epilogue, then the listener records the compiled method id of the
current method.
</p><!--l. 17--><p class="noindent" >When the buffer of samples is full, the sampling window ends. The listener then
unregisters itself (stops taking samples) and wakes the sleeping Hot Method
Organizer. The Hot Method Organizer processes the buffer of compiled method ids
by updating the Method Sample Data. This data structure maintains, for every
compiled method, the total number of times that it has been sampled. Careful design
of this data structure (<span class="obeylines-h"><span class="verb"><span
class="cmtt-10">MethodCountData.java</span></span></span>) was critical to achieving low profiling
overhead. In addition to supporting lookups and updates by compiled method id, it
must also efficiently enumerate all methods that have been sampled more times
than a (varying) threshold value. After updating the Method Sample Data,
the Hot Method Organizer creates an event for each method that has been
sampled in this window and adds it to the controller’s priority queue, using the
sample value as its priority. The event contains the compiled method and the
<span
class="cmti-10">total </span>number of times it has been sampled since the beginning of execution.
After enqueuing the last event, the Hot Method Organizer re-registers the
method listener and then sleeps until the next buffer of samples is ready to be
processed.
</p><!--l. 19--><p class="noindent" >When the priority queue delivers an event to the controller, the controller dequeues
the event and applies the model-driven recompilation policy to determine what
action (if any) to take for the indicated method. If the controller decides to recompile
the method, it creates a recompilation event that describes the method to be
compiled and the optimization plan to use and places it on the recompilation
queue. The recompilation queue prioritizes events based on the cost-benefit
computation.
</p><!--l. 21--><p class="noindent" >When an event is available on the recompilation queue, the recompilation thread
removes it and performs the compilation activity specified by the event. It invokes
the optimizing compiler at the specified optimization level and installs the resulting
compiled method into the VM.
</p><!--l. 23--><p class="noindent" >Although the overall structure of selective optimization in Jikes RVM is similar to
that originally described in Arnold et al’s OOPSLA 2000 paper, we have made
several changes and improvements based on further experience with the system. The
most significant change is that in the previous system, the method sample organizer
attempted to filter the set of methods it presented to the controller. The
organizer passed along to the controller only methods considered ”hot”. The
organizer deemed a method ”hot” if the percentage of samples attributed
to the method exceeded a dynamically adjusted threshold value. Method
samples were periodically decayed to give more weight to recent samples.
The controller dynamically adjusted this threshold value and the size of the
sampling window in an attempt to reduce the overhead of processing the
samples.
</p><!--l. 25--><p class="noindent" >Later, significant algorithmic improvements in key data structures and additional
performance tuning of the listeners, organizers, and controller reduced AOS overhead
by two orders of magnitude. These overhead reductions obviate the need
to filter events passed to the controller. This resulted in a more effective
system with fewer parameters to tune and a sounder theoretical basis. In
general, as we gained experience with the adaptive system implementation, we
strove to reduce the number of tuning parameters. We believe that the closer
the implementation matches the basic theoretical cost-benefit model, the
more likely it will perform well and make reasonable and understandable
decisions.
</p>
<!--l. 31--><p class="noindent" ><span class="paragraphHead"><a
id="x15-15100012"></a><span
class="cmbx-10">Profile-Directed Inlining</span></span>
Profile-directed inlining attempts to identify frequently traversed call graph edges,
which represent caller-callee relationships, and determine whether it is beneficial to
recompile the caller methods to allow inlining of the callee methods. In Jikes RVM,
profile-directed inlining augments a number of static inlining heuristics. The
role of profile-directed inlining is to identify high cost-high benefit inlining
opportunities that evade the static heuristics and to predict the likely target(s) of
invokevirtual and invokeinterface calls that could not be statically bound at compile
time.
</p><!--l. 37--><p class="noindent" >To accomplish this goal, the system takes a statistical sample of the method calls in
the running application and maintains an approximation of the dynamic call graph
based on this data. The system installs a listener that samples call edges whenever
a yieldpoint is taken in the prologue or epilogue of a method. To sample
the call edge, it records the compiled method id of the caller and callee
methods and the offset of the call instruction in the caller’s machine code into
a buffer. When the buffer of samples is full, the sampling window ends.
The listener then unregisters itself (stops taking samples) and wakes an
organizer to update the dynamic call graph with the new profile data. The
optimizing compiler’s Inline Oracle uses the dynamic call graph to guide it’s inline
decisions.
</p><!--l. 40--><p class="noindent" >The system currently used is based on Arnold & Grove’s CGO 2005 paper. More
details of the sampling scheme and the inlining oracle can be found there, or in the
source code.
</p><!--l. 2--><p class="noindent" >
</p>
<h3 class="sectionHead"><span class="titlemark">12.1 </span> <a
id="x15-15200012.1"></a>AOS Controller</h3>
<!--l. 5--><p class="noindent" >A primary design goal for the adaptive optimization system is to enable research in
online feedback-directed optimization. Therefore, we require the controller
implementation to be flexible and extensible. As we gained experience with the
system, the controller component went through several major redesigns to better
support our goals.
</p><!--l. 7--><p class="noindent" >The controller is a single Java thread that runs an infinite event loop. After
initializing AOS, the controller enters the event loop and attempts to dequeue an
event. If no event is available, the dequeue operation blocks (suspending the
controller thread) until an event is available. All controller events implement an
interface with a single method: process. Thus, after successfully dequeuing an event
the controller thread simply invokes its process method and then, the work for that
event having been completed, returns to the top of the event loop and attempts to
dequeue another event. This design makes it easy to add new kinds of events to the
system (and thus, extend the controller’s behavior), as all of the logic to process an
event is defined by the event’s process method, not in the code of the controller
thread.
</p><!--l. 9--><p class="noindent" >A further level of abstraction is accomplished by representing the recompilation
strategy as an abstract class with several subclasses. The process method of a hot
method event invokes methods of the recompilation strategy to determine whether or
not a method should be recompiled, and if so at what optimization level. The
cost-benefit model itself is also reified in a class hierarchy of models to enable
extension and variation. This set of abstractions enable a single controller
implementation to execute a variety of strategies.
</p><!--l. 11--><p class="noindent" >Another useful mechanism for experimentation is the ability to easily change the
input parameters to AOS that define the expected compilation rates and
execution speed of compiled code for the various compilers. By varying these
parameters, one can easily cause the default multi-level cost-benefit model to
simulate a single-level model (by defining all but one optimization level to be
unprofitable). One can also explore other aspects of the system, for example the
sensitivity of the model to the accuracy of these parameters. We found this
capability to be so useful that the system supports a command line argument
(<span class="obeylines-h"><span class="verb"><span
class="cmtt-10">-X:aos:dna=<filename></span></span></span>) that causes it to optionally read these parameters from a
file.
</p><!--l. 2--><p class="noindent" >
</p>
<h3 class="sectionHead"><span class="titlemark">12.2 </span> <a
id="x15-15300012.2"></a>Cost Benefit Model</h3>
<!--l. 3--><p class="noindent" >The Jikes RVM Adaptive Optimization System attempts to evaluate the break-even
point for each action using an online competitive algorithm. It relies on an analytic
model to estimate the costs and benefits of each selective recompilation
action, and evaluates the best actions according to the model predictions
online.
</p><!--l. 5--><p class="noindent" >A key advantage of this approach is that it allows a designer to extend the simple
”break-even” cost-benefit model to account for more sophisticated adaptive policies,
such as selective compilation with multiple optimization levels, on-stack-replacement,
and long-running analyses.
</p><!--l. 7--><p class="noindent" >In general, each potential action will incur some cost and may confer some benefit.
For example, recompiling a method will certainly consume some CPU cycles, but
could speed up the program execution by generating better code. In this
discussion we focus on costs and benefits defined in terms of time (CPU
cycles). However, in general, the controller could consider other measures of
cost and benefit, such as memory footprint, garbage allocated, or locality
disrupted.
</p><!--l. 9--><p class="noindent" >The controller will take some action when it estimates the benefit to exceed the cost.
More precisely, when the controller wakes at time <span
class="cmmi-10">t</span>, it considers a set of <span
class="cmmi-10">n </span>available
actions, the set <span
class="cmmi-10">A </span>= <span
class="cmsy-10">{</span><span
class="cmmi-10">A</span><sub><span
class="cmr-7">1</span></sub><span
class="cmmi-10">,A</span><sub><span
class="cmr-7">2</span></sub><span
class="cmmi-10">,...,A</span><sub><span
class="cmmi-7">n</span></sub><span
class="cmsy-10">}</span>. For any subset <span
class="cmmi-10">S </span>in <span
class="cmmi-10">P</span>(<span
class="cmmi-10">A</span>), the controller can
estimate the cost <span
class="cmmi-10">C</span>(<span
class="cmmi-10">S</span>) and benefit <span
class="cmmi-10">B</span>(<span
class="cmmi-10">S</span>) of performing all actions <span
class="cmmi-10">A</span><sub><span
class="cmmi-7">i</span></sub> in <span
class="cmmi-10">S</span>. The
controller will attempt to choose the subset <span
class="cmmi-10">S </span>that maximizes <span
class="cmmi-10">B</span>(<span
class="cmmi-10">S</span>) <span
class="cmsy-10">− </span><span
class="cmmi-10">C</span>(<span
class="cmmi-10">S</span>).
Obviously <span
class="cmmi-10">S </span>= <span
class="cmsy-10">{} </span>has <span
class="cmmi-10">B</span>(<span
class="cmmi-10">S</span>) = <span
class="cmmi-10">C</span>(<span
class="cmmi-10">S</span>) = 0; the controller takes no action if it cannot
find a profitable course.
</p><!--l. 11--><p class="noindent" >In practice, the precise cost and benefit of each action cannot be known; so, the
controller must rely on estimates to make decisions.
</p><!--l. 13--><p class="noindent" >The basic model the controller uses to decide which method to recompile, at which
optimization level, and at what time is as follows.
</p><!--l. 15--><p class="noindent" >Suppose that when the controller wakes at time <span
class="cmmi-10">t</span>, and each method <span
class="cmmi-10">m </span>is currently
optimized at optimization level <span
class="cmmi-10">m</span><sub><span
class="cmmi-7">i</span></sub><span
class="cmmi-10">,</span>0 <span
class="cmsy-10">≤ </span><span
class="cmmi-10">i </span><span
class="cmsy-10">≤ </span><span
class="cmmi-10">k</span>. Let <span
class="cmmi-10">M </span>be the set of loaded methods in
the program. Let <span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub> be the action ”recompile method m at optimization level <span
class="cmmi-10">j</span>, or
do nothing if <span
class="cmmi-10">j </span>= <span
class="cmmi-10">i</span>.”
</p><!--l. 17--><p class="noindent" >The controller must choose an action for each <span
class="cmmi-10">m </span>in <span
class="cmmi-10">M</span>. The set of available actions is
<span
class="cmmi-10">Actions </span>= <span
class="cmsy-10">{</span><span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub><span
class="cmsy-10">|</span>0 <span
class="cmsy-10">≤ </span><span
class="cmmi-10">j </span><span
class="cmsy-10">≤ </span><span
class="cmmi-10">k,m </span><span
class="cmsy-10">∈ </span><span
class="cmmi-10">M</span><span
class="cmsy-10">}</span>.
</p><!--l. 19--><p class="noindent" >Each action has an estimated cost and benefit: <span
class="cmmi-10">C</span>(<span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub>), the cost of taking action
<span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub>, for 0 <span
class="cmsy-10">≤ </span><span
class="cmmi-10">j </span><span
class="cmsy-10">≤ </span><span
class="cmmi-10">k </span>and <span
class="cmmi-10">T</span>(<span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub>), the expected time the program will spend executing
method <span
class="cmmi-10">m </span>in the future, if the controller takes action <span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub>.
</p><!--l. 21--><p class="noindent" >For <span
class="cmmi-10">S </span>in <span
class="cmmi-10">Actions</span>, define <span
class="cmmi-10">C</span>(<span
class="cmmi-10">S</span>) = <span
class="cmex-10">∑</span>
<sub><span
class="cmmi-7">s</span><span
class="cmsy-7">∈</span><span
class="cmmi-7">S</span></sub><span
class="cmmi-10">C</span>(<span
class="cmmi-10">s</span>). Given <span
class="cmmi-10">S</span>, for each <span
class="cmmi-10">m </span>in <span
class="cmmi-10">M</span>,
define <span
class="cmmi-10">A</span><sub><span
class="cmmi-7">min</span><sub><span
class="cmmi-5">m</span></sub></sub> to be the action <span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub> in <span
class="cmmi-10">S </span>that minimizes <span
class="cmmi-10">T</span>(<span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub>). Then define
<span
class="cmmi-10">T</span>(<span
class="cmmi-10">S</span>) = <span
class="cmex-10">∑</span>
<sub><span
class="cmmi-7">m</span><span
class="cmsy-7">∈</span><span
class="cmmi-7">M</span></sub><span
class="cmmi-10">T</span>(<span
class="cmmi-10">A</span><sub><span
class="cmmi-7">min</span><sub><span
class="cmmi-5">m</span></sub></sub>).
</p><!--l. 23--><p class="noindent" >Using these estimated values, the controller chooses the set <span
class="cmmi-10">S </span>that minimizes
<span
class="cmmi-10">C</span>(<span
class="cmmi-10">S</span>) + <span
class="cmmi-10">T</span>(<span
class="cmmi-10">S</span>). Intuitively, for each method <span
class="cmmi-10">m</span>, the controller chooses the recompilation
level <span
class="cmmi-10">j </span>that minimizes the expected future compilation time and running time of
<span
class="cmmi-10">m</span>.
</p><!--l. 25--><p class="noindent" >It remains to define the functions <span
class="cmmi-10">C </span>and <span
class="cmmi-10">T </span>for each recompilation action. The basic
model models the cost <span
class="cmmi-10">C </span>of compiling a method <span
class="cmmi-10">m </span>at level <span
class="cmmi-10">j </span>as a linear function of
the size of <span
class="cmmi-10">m</span>. The linear function is determined by an offline experiment to fit
constants to the model.
</p><!--l. 27--><p class="noindent" >The basic model estimates that the speedup for any optimization level <span
class="cmmi-10">j </span>is constant.
The implementation determines the constant speedup factor for each optimization
level offline, and uses the speedup to compute <span
class="cmmi-10">T </span>for each method and optimization
level.
</p><!--l. 29--><p class="noindent" >We assume that if the program has run for time <span
class="cmmi-10">t</span>, then the program will run for
another <span
class="cmmi-10">t </span>units, and then terminate. We further assume program behavior in the
future will resemble program behavior in the past. Therefore, for each method we
estimate that if no optimization action is performed <span
class="cmmi-10">T</span>(<span
class="cmmi-10">A</span><sub><span
class="cmmi-7">jm</span></sub>) is equal to the time
spent executing method <span
class="cmmi-10">m </span>so far.
</p><!--l. 31--><p class="noindent" >Let <span
class="cmmi-10">M </span>= (<span
class="cmmi-10">m</span><sub><span
class="cmr-7">1</span></sub><span
class="cmmi-10">,...,m</span><sub><span
class="cmmi-7">k</span></sub>) be the <span
class="cmmi-10">k </span>compiled methods. When the controller wakes at time
<span
class="cmmi-10">t</span>, each compiled method <span
class="cmmi-10">m </span>has been sampled <span
class="cmex-10">∑</span>
<span
class="cmmi-10">m </span>times. Let <span
class="cmmi-10">δ </span>be the sampling
interval, measured in seconds. The controller estimates that method <span
class="cmmi-10">m </span>has executed
<span
class="cmmi-10">δ</span> <span
class="cmex-10">∑</span>
<span
class="cmmi-10">m </span>seconds so far, and will execute for another <span
class="cmmi-10">δ</span> <span
class="cmex-10">∑</span>
<span
class="cmmi-10">m </span>seconds in the
future.
</p><!--l. 33--><p class="noindent" >When driving recompilation based on sampling, the controller can limit its attention
to the set of methods that were sampled in the previous sampling interval. This
optimization does not lose precision; if the number of samples associated with a
method has not changed, then the controller’s estimate of the method’s future
execution time will not change. This implies that if the controller were to consider a
method that does not appear in the previous sampling interval, the controller would
make exactly the same decision it did the last time it considered the method. This
optimization, limiting the number of methods the controller must examine in each
sampling interval, greatly reduces the amount of work performed by the
controller.
</p><!--l. 36--><p class="noindent" >Suppose the controller recompiles method m from optimization level <span
class="cmmi-10">i </span>to
optimization level <span
class="cmmi-10">j </span>after having seen <span
class="cmex-10">∑</span>
<span
class="cmmi-10">m </span>samples. Let <span
class="cmmi-10">S</span><sub><span
class="cmmi-7">i</span></sub> and <span
class="cmmi-10">S</span><sub><span
class="cmmi-7">j</span></sub>be the speedup
ratios for optimization levels <span
class="cmmi-10">i </span>and <span
class="cmmi-10">j</span>, respectively. After optimizing at level <span
class="cmmi-10">j</span>, we
adjust the sample data to represent the system state as if it had executed
method <span
class="cmmi-10">m </span>at optimization level <span
class="cmmi-10">j </span>since program startup. So, we set the new
number of samples for <span
class="cmmi-10">m </span>to be <span
class="cmex-10">∑</span>
<span
class="cmmi-10">m </span><span
class="cmsy-10">⋅ </span>(<span
class="cmmi-10">S</span><sub><span
class="cmmi-7">i</span></sub><span
class="cmmi-10">∕S</span><sub><span
class="cmmi-7">j</span></sub>). Thus to compute the time spent
in <span
class="cmmi-10">m</span>, we need know only one number, the ”effective” number of samples.
</p><!--l. 2--><p class="noindent" >
</p>
<h3 class="sectionHead"><span class="titlemark">12.3 </span> <a
id="x15-15400012.3"></a>Jikes RVM’s compilers</h3>
<!--l. 5--><p class="noindent" >Jikes RVM invokes a compiler for one of three reasons. First, when the executing
code reaches an unresolved reference, causing a new class to be loaded, the
class loader invokes a compiler to compile the class initializer (if one exists).
Second, the system compiles each method the first time it is invoked. In these
first two scenarios, the initiating application thread stalls until compilation
completes.
</p><!--l. 8--><p class="noindent" >In the third scenario, the adaptive optimization system can invoke a compiler when
profiling data suggests that <span
class="cmti-10">recompiling </span>a method with additional optimizations may
be beneficial. The system supports both background and foreground recompilation.
With background recompilation (the default), a dedicated thread asynchronously
performs all recompilations. With foreground configuration, the system invalidates a
compiled method, thus, forcing recompilation at the desired optimization
level at the next invocation (stalling the invoking thread until compilation
completes).
</p><!--l. 10--><p class="noindent" >The system includes two compilers with different tradeoffs between compilation
overhead and code quality. </p>
<ul class="itemize1">
<li class="itemize">The goal of the <span
class="cmti-10">baseline </span>compiler is to generate correct code quickly.
For example, the IA32 baseline compiler translates bytecodes directly
into native code by simulating Java’s operand stack. It does not build
an intermediate representation and does not perform register allocation,
resulting in native code that executes only somewhat faster than bytecode
interpretation. However, it does achieve its goal of producing this code
quickly, which significantly reduces the initial overhead associated with
dynamic compilation.
</li>
<li class="itemize">The <span
class="cmti-10">optimizing </span>compiler translates bytecodes into an intermediate
representation, upon which it performs a variety of optimizations. All
optimization levels include linear scan register allocation and BURS-based
instruction selection. The compiler’s optimizations are grouped into several
levels:
<ul class="itemize2">
<li class="itemize"><span
class="cmbx-10">Level 0 </span>consists of a set of flow-sensitive optimizations performed
on-the-fly during the translation from bytecodes to the intermediate
representation and some additional optimizations that are either
highly effective or have negligible compilation costs. The compiler
performs the following optimizations during IR generation: constant,
type, non-null, and copy propagation, constant folding and arithmetic
simplification, branch optimizations, field analysis, unreachable code
elimination, inlining of trivial methods (A trivial method is one
whose body is estimated to take less code space than 2 times
the size of a calling sequence and that can be inlined without an
explicit guard.), elimination of redundant nullchecks, checkcasts, and
array store checks. As these optimizations reduce the size of the
generated IR, performing them tends to reduce overall compilation
time. Level 0 includes a number of cheap local (The scope of a
local optimization is one extended basic block.) optimizations such
as local redundancy elimination (common subexpression elimination,
loads, and exception checks), copy propagation, constant propagation
and folding. Level 0 also includes simple control flow optimizations
such as static basic block splitting, peephole branch optimization,
and tail recursion elimination. Finally, Level 0 performs simple code
reordering, scalar replacement of aggregates and short arrays, and one
pass of intraprocedural flow-insensitive copy propagation, constant
propagation, and dead assignment elimination.
</li>
<li class="itemize"><span
class="cmbx-10">Level 1 </span>resembles Level 0, but significantly increases the
aggressiveness of inlining heuristics. The compiler performs both
unguarded inlining of final and static methods and (speculative)
guarded inlining of non-final virtual and interface methods.
Speculative inlining is driven both by class hierarchy analysis and
online profile data gathered by the adaptive system. In addition, the
compiler exploits “preexistence” to safely perform unguarded inlining
of some invocations of non-final virtual methods <span
class="cmti-10">without </span>requiring
stack frame rewriting on invalidation. It also runs multiple passes of
some of the Level 0 optimizations and uses a more sophisticated code
reordering algorithm due to Pettis and Hansen.
</li>
<li class="itemize"><span
class="cmbx-10">Level 2 </span>augments level 1 with loop optimizations such
as normalization and unrolling; scalar SSA-based flow-sensitive
optimizations based on dataflow, global value numbering, global
common subexpression elimination, redundant and conditional
branch elimination; and heap array SSA-based optimizations, such as
load/store elimination, and global code placement. <span
class="cmbx-10">NOTE: many</span>
<span
class="cmbx-10">of the O2 optimizations are disabled by default by defining</span>
<span
class="cmbx-10">them as O3 optimizations because they are believed to be</span>
<span
class="cmbx-10">somewhat buggy.</span></li></ul>
</li></ul>
<!--l. 21--><p class="noindent" >The adaptive system uses information about average compilation rate and relative
speed of compiled code produced by each compiler/optimization level to make its
decisions. These characteristics of the compilers are the key inputs to enable selective
optimization to be effective. It allows one to employ a quick executing compiler for
infrequently executed methods and an optimizing compiler for the most critical
methods. See <span
class="cmtt-10">org.jikesrvm.adaptive.recompilation.CompilerDNA </span>for the
current values of these input parameters to the adaptive systems cost/benefit
model.
</p><!--l. 2--><p class="noindent" >
</p>
<h3 class="sectionHead"><span class="titlemark">12.4 </span> <a
id="x15-15500012.4"></a>Life Cycle of a Compiled Method</h3>
<!--l. 4--><p class="noindent" >In early implementations of Jikes RVM’s adaptive system, compilation required
holding a global lock that serialized compilation and also prevented classloading from
occurring concurrently with compilation. This bottleneck was removed in version
2.1.0 by switching to a finer-grained locking discipline to coordinate compilation,
speculative optimization, and class loading. Since no published description of this
locking protocol exists outside of the source code, we briefly summarize the life cycle
of a compiled method here.
</p><!--l. 6--><p class="noindent" >When Jikes RVM compiles a method, it creates a compiled method object to
represent this particular compilation of the source method. A compiled method has a
unique id, and stores the compiled code and associated compiler meta-data. After a
brief initialization phase, the compiled method transitions from uncompiled to
compiling when compilation begins. During compilation, the optimizing
compiler may perform speculative optimizations that can be invalidated
by future class loading. Each time the compiler so speculates, it records a
relevant entry in an invalidation database. Upon finishing compilation, the
system checks to ensure that the current compilation has not already been
invalidated by concurrent classloading. If it has not, then the system installs the
compiled code, and subsequent invocations will branch to the newly created
code.
</p><!--l. 8--><p class="noindent" >Each time a class is loaded, the system checks the invalidation database
to identify the set of compiled methods to mark as obsolete, because this
classloading action invalidates speculative optimizations previously applied to that
method. A method may transition from either compiling or installed to obsolete
due to a classloading-induced invalidation. A method can also transition
from installed to obsolete when the adaptive system selects a method for
optimizing recompilation and a new compiled method is installed to replace
it.
</p>
<hr class="figure" /><div class="figure"
>
<a
id="x15-155001r1"></a>
<!--l. 13--><p class="noindent" ><img
src="/UserGuide/images/93224965.png" alt="PIC"
width="345" height="240" />
<br /> </p><div class="caption"
><span class="id">Figure 12.1: </span><span
class="content">life cycle of a compiled method</span></div><!--tex4ht:label?: x15-155001r1 -->
</div><hr class="endfigure" />
<!--l. 17--><p class="noindent" >Once a method is marked obsolete, it will never be invoked again. However, before
the generated code for the compiled method can be garbage collected, all existing
invocations of the compiled method must be complete. A compiled method
transitions from obsolete to dead when no invocations of it exist on any thread
stack. Jikes RVM detects this as part of the stack scanning phase of garbage
collection; as stack frames are scanned, their compiled methods are marked
as active. Any obsolete method that is not marked as active when stack
scanning completes is marked as dead and the reference to it is removed from
the compiled method table. It will then be freed during the next garbage
collection.
</p>
<h3 class="sectionHead"><span class="titlemark">12.5 </span> <a
id="x15-15600012.5"></a>Logging and Debugging</h3>
<!--l. 5--><p class="noindent" >Complex non-deterministic systems such as the Jikes RVM adaptive system present
challenges for system understanding and debugging. Virtually all of the
profiling data collected by the runtime measurements component results from
non-deterministic timer-based sampling at taken yieldpoints. The exact timing of
these interrupts, and thus, the profile data that drives recompilation decisions,
differs somewhat each time an application executes. Furthermore, many
of the optimizations in the optimizing compiler rely on online profiles of
conditional branch probabilities, i.e., the probabilities at the point in an
execution when the recompilation occurs. Thus, because recompilations can
occur at different times during each execution, a method compiled at the
same optimization level could be compiled slightly differently on different
runs.
</p><!--l. 7--><p class="noindent" >The primary mechanism we use to manage this complexity is a record-replay facility
for the adaptive system, where online profile data is gathered during one run and
used in a subsequent run. More specifically, as methods are dynamically compiled,
the system can record this information into a log file. At the end of the run, the
system can optionally dump the branch probabilities of all instrumented conditional
branches, the profile-derived call graph, and the profile-directed inlining decisions.
This log of methods and the files of profile data can then be provided as inputs to a
driver program (<span
class="cmtt-10">org.jikesrvm.tools.opt.OptTestHarness</span>) that can replay
the series of compilation actions, and then optionally execute the program.
Usually a fairly rapid binary search of methods being compiled and/or the
supporting profile data suffices to narrow the cause of a crash to a small set of
actions taken by the optimizing compiler. Although this does not enable a
perfectly accurate replay of a previous run, in practice, we have found that it
suffices to reproduce almost all crashes caused by bugs in the optimizing
compiler.
</p><!--l. 9--><p class="noindent" >In addition to this record-replay mechanism, which mainly helps debugging the
optimizing compiler, the adaptive system can generate a log file that contains
detailed information about the actions of its organizer and controller threads. A
sample is shown below:
</p>
<!--l. 11-->
<div class="lstlisting" id="listing-100"><span class="label"><a
id="x15-156001r1"></a></span><span
class="cmtt-10">30:..7047728888</span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiled</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">with</span><span
class="cmtt-10"> </span><span
class="cmtt-10">baseline</span><span
class="cmtt-10"> </span><span
class="cmtt-10">compiler</span><span
class="cmtt-10"> </span><span
class="cmtt-10">in</span><span
class="cmtt-10"> </span><span
class="cmtt-10">0.20</span><span
class="cmtt-10"> </span><span
class="cmtt-10">ms</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156002r2"></a></span><span
class="cmtt-10">90:..7136817287</span><span
class="cmtt-10"> </span><span
class="cmtt-10">Controller</span><span
class="cmtt-10"> </span><span
class="cmtt-10">notified</span><span
class="cmtt-10"> </span><span
class="cmtt-10">that</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10">(14402)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">has</span><span
class="cmtt-10"> </span><span
class="cmtt-10">4.0</span><span
class="cmtt-10"> </span><span
class="cmtt-10">samples</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156003r3"></a></span><span
class="cmtt-10">92:..7139813016</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Doing</span><span
class="cmtt-10"> </span><span
class="cmtt-10">nothing</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">(</span><span
class="cmtt-10">leaving</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">baseline</span><span
class="cmtt-10">)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">to</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">is</span><span
class="cmtt-10"> </span><span
class="cmtt-10">40.0</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156004r4"></a></span><span
class="cmtt-10">92:..7139830219</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O0</span><span
class="cmtt-10">=40.42,</span><span
class="cmtt-10"> </span><span
class="cmtt-10">future</span><span
class="cmtt-10"> </span><span
class="cmtt-10">time</span><span
class="cmtt-10">=49.81</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156005r5"></a></span><span
class="cmtt-10">92:..7139842466</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O1</span><span
class="cmtt-10">=65.99,</span><span
class="cmtt-10"> </span><span
class="cmtt-10">future</span><span
class="cmtt-10"> </span><span
class="cmtt-10">time</span><span
class="cmtt-10">=72.58</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156006r6"></a></span><span
class="cmtt-10">92:..7139854029</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O2</span><span
class="cmtt-10">=207.44,</span><span
class="cmtt-10"> </span><span
class="cmtt-10">future</span><span
class="cmtt-10"> </span><span
class="cmtt-10">time</span><span
class="cmtt-10">=213.49</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156007r7"></a></span><span
class="cmtt-10">110:..7166901172</span><span
class="cmtt-10"> </span><span
class="cmtt-10">Controller</span><span
class="cmtt-10"> </span><span
class="cmtt-10">notified</span><span
class="cmtt-10"> </span><span
class="cmtt-10">that</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10">(14402)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">has</span><span
class="cmtt-10"> </span><span
class="cmtt-10">9.0</span><span
class="cmtt-10"> </span><span
class="cmtt-10">samples</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156008r8"></a></span><span
class="cmtt-10">111:..7168378722</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Doing</span><span
class="cmtt-10"> </span><span
class="cmtt-10">nothing</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">(</span><span
class="cmtt-10">leaving</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">baseline</span><span
class="cmtt-10">)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">to</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10">=90.0</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156009r9"></a></span><span
class="cmtt-10">111:..7168396493</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O0</span><span
class="cmtt-10">=40.42,</span><span
class="cmtt-10"> </span><span
class="cmtt-10">future</span><span
class="cmtt-10"> </span><span
class="cmtt-10">time</span><span
class="cmtt-10">=61.54</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156010r10"></a></span><span
class="cmtt-10">111:..7168409562</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O1</span><span
class="cmtt-10">=65.99,</span><span
class="cmtt-10"> </span><span
class="cmtt-10">future</span><span
class="cmtt-10"> </span><span
class="cmtt-10">time</span><span
class="cmtt-10">=80.81</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156011r11"></a></span><span
class="cmtt-10">111:..7168421097</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O2</span><span
class="cmtt-10">=207.44,</span><span
class="cmtt-10"> </span><span
class="cmtt-10">future</span><span
class="cmtt-10"> </span><span
class="cmtt-10">time</span><span
class="cmtt-10">=221.06</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156012r12"></a></span><span
class="cmtt-10">111:..7168435937</span><span
class="cmtt-10"> </span><span
class="cmtt-10">Scheduling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">level</span><span
class="cmtt-10"> </span><span
class="cmtt-10">0</span><span
class="cmtt-10"> </span><span
class="cmtt-10">recompilation</span><span
class="cmtt-10"> </span><span
class="cmtt-10">of</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">(</span><span
class="cmtt-10">priority</span><span
class="cmtt-10">=28.46)</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156013r13"></a></span><span
class="cmtt-10">112:..7169879779</span><span
class="cmtt-10"> </span><span
class="cmtt-10">Recompiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">(</span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">level</span><span
class="cmtt-10"> </span><span
class="cmtt-10">0)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156014r14"></a></span><span
class="cmtt-10">114:..7173293360</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Recompiled</span><span
class="cmtt-10"> </span><span
class="cmtt-10">(</span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">level</span><span
class="cmtt-10"> </span><span
class="cmtt-10">0)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156015r15"></a></span><span
class="cmtt-10">150:..7227058078</span><span
class="cmtt-10"> </span><span
class="cmtt-10">Controller</span><span
class="cmtt-10"> </span><span
class="cmtt-10">notified</span><span
class="cmtt-10"> </span><span
class="cmtt-10">that</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10">(14612)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">has</span><span
class="cmtt-10"> </span><span
class="cmtt-10">5.11</span><span
class="cmtt-10"> </span><span
class="cmtt-10">samples</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156016r16"></a></span><span
class="cmtt-10">151:..7228691160</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Doing</span><span
class="cmtt-10"> </span><span
class="cmtt-10">nothing</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">(</span><span
class="cmtt-10">leaving</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O0</span><span
class="cmtt-10">)</span><span
class="cmtt-10"> </span><span
class="cmtt-10">to</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10">=51.12</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156017r17"></a></span><span
class="cmtt-10">151:..7228705466</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span
class="cmtt-10"> </span><span
class="cmtt-10">read</span><span
class="cmtt-10"> </span><span
class="cmtt-10">cost</span><span
class="cmtt-10"> </span><span
class="cmtt-10">at</span><span
class="cmtt-10"> </span><span
class="cmtt-10">O1</span><span
class="cmtt-10">=66.26,</span><span
class="cmtt-10"> </span><span
class="cmtt-10">future</span><span
class="cmtt-10"> </span><span
class="cmtt-10">time</span><span
class="cmtt-10">=102.14</span><span
class="cmtt-10"> </span><br /><span class="label"><a
id="x15-156018r18"></a></span><span
class="cmtt-10">151:..7228717124</span><span
class="cmtt-10"> </span><span
class="cmtt-10"> </span><span
class="cmtt-10">Compiling</span><span