@@ -251,16 +251,34 @@ public boolean visit(JNode x, Context ctx) {
251
251
252
252
private static final String ENUM_NAME_OBFUSCATION_PROPERTY = "compiler.enum.obfuscate.names" ;
253
253
254
+ /**
255
+ * Continuing to apply optimizations till the rate of change reaches this value causes the AST to
256
+ * reach a fixed point.
257
+ */
258
+ private static final int FIXED_POINT_CHANGE_RATE = 0 ;
259
+
260
+ /**
261
+ * Ending optimization passes when the rate of change has reached this value results in
262
+ * gaining nearly all of the impact while avoiding the long tail of costly but low-impact passes.
263
+ */
264
+ private static final float EFFICIENT_CHANGE_RATE = 0.01f ;
265
+
266
+ /**
267
+ * Limits the number of optimization passes against the possible danger of an AST that does not
268
+ * converge.
269
+ */
270
+ private static final int MAX_PASSES = 100 ;
271
+
254
272
/**
255
273
* Compiles a particular permutation, based on a precompiled unified AST.
256
274
*
257
275
* @param logger the logger to use
258
- * @param unifiedAst the result of a
259
- * {@link #precompile(TreeLogger, ModuleDef, RebindPermutationOracle, String[], String[], JJSOptions, boolean, PrecompilationMetricsArtifact)}
276
+ * @param unifiedAst the result of a {@link #precompile(TreeLogger, ModuleDef,
277
+ * RebindPermutationOracle, String[], String[], JJSOptions, boolean,
278
+ * PrecompilationMetricsArtifact)}
260
279
* @param permutation the permutation to compile
261
280
* @return the output JavaScript
262
- * @throws UnableToCompleteException if an error other than
263
- * {@link OutOfMemoryError} occurs
281
+ * @throws UnableToCompleteException if an error other than {@link OutOfMemoryError} occurs
264
282
*/
265
283
public static PermutationResult compilePermutation (TreeLogger logger , UnifiedAst unifiedAst ,
266
284
Permutation permutation ) throws UnableToCompleteException {
@@ -768,11 +786,16 @@ protected static void optimize(JJSOptions options, JProgram jprogram)
768
786
Event optimizeEvent = SpeedTracerLogger .start (CompilerEventType .OPTIMIZE );
769
787
770
788
List <OptimizerStats > allOptimizerStats = new ArrayList <OptimizerStats >();
771
- int counter = 0 ;
772
- int optimizationLevel = options .getOptimizationLevel ();
789
+ int passCount = 0 ;
790
+ int nodeCount = getNodeCount (jprogram );
791
+ int lastNodeCount ;
792
+
793
+ boolean atMaxLevel = options .getOptimizationLevel () == OptionOptimize .OPTIMIZE_LEVEL_MAX ;
794
+ int passLimit = atMaxLevel ? MAX_PASSES : options .getOptimizationLevel ();
795
+ float minChangeRate = atMaxLevel ? FIXED_POINT_CHANGE_RATE : EFFICIENT_CHANGE_RATE ;
773
796
while (true ) {
774
- counter ++;
775
- if (optimizationLevel < OptionOptimize . OPTIMIZE_LEVEL_MAX && counter > optimizationLevel ) {
797
+ passCount ++;
798
+ if (passCount > passLimit ) {
776
799
break ;
777
800
}
778
801
if (Thread .interrupted ()) {
@@ -781,9 +804,14 @@ protected static void optimize(JJSOptions options, JProgram jprogram)
781
804
}
782
805
AstDumper .maybeDumpAST (jprogram );
783
806
OptimizerStats stats =
784
- optimizeLoop ("Pass " + counter , jprogram , options .isAggressivelyOptimize ());
807
+ optimizeLoop ("Pass " + passCount , jprogram , options .isAggressivelyOptimize (), nodeCount );
785
808
allOptimizerStats .add (stats );
786
- if (!stats .didChange ()) {
809
+ lastNodeCount = nodeCount ;
810
+ nodeCount = getNodeCount (jprogram );
811
+
812
+ float nodeChangeRate = stats .getNumMods () / (float ) lastNodeCount ;
813
+ float sizeChangeRate = (lastNodeCount - nodeCount ) / (float ) lastNodeCount ;
814
+ if (nodeChangeRate <= minChangeRate && sizeChangeRate <= minChangeRate ) {
787
815
break ;
788
816
}
789
817
}
@@ -849,15 +877,15 @@ protected static void optimizeJs(JJSOptions options, JsProgram jsProgram,
849
877
850
878
protected static OptimizerStats optimizeLoop (String passName , JProgram jprogram ,
851
879
boolean isAggressivelyOptimize ) {
852
- Event optimizeEvent = SpeedTracerLogger .start (CompilerEventType .OPTIMIZE , "phase" , "loop" );
853
-
854
- // Count the number of nodes in the AST so we can measure the efficiency of
855
- // the optimizers.
856
- Event countEvent = SpeedTracerLogger .start (CompilerEventType .OPTIMIZE , "phase" , "countNodes" );
857
880
TreeStatistics treeStats = new TreeStatistics ();
858
881
treeStats .accept (jprogram );
859
882
int numNodes = treeStats .getNodeCount ();
860
- countEvent .end ();
883
+ return optimizeLoop ("Early Optimization" , jprogram , false , numNodes );
884
+ }
885
+
886
+ protected static OptimizerStats optimizeLoop (String passName , JProgram jprogram ,
887
+ boolean isAggressivelyOptimize , int numNodes ) {
888
+ Event optimizeEvent = SpeedTracerLogger .start (CompilerEventType .OPTIMIZE , "phase" , "loop" );
861
889
862
890
// Recompute clinits each time, they can become empty.
863
891
jprogram .typeOracle .recomputeAfterOptimizations ();
@@ -911,6 +939,15 @@ protected static OptimizerStats optimizeLoop(String passName, JProgram jprogram,
911
939
return stats ;
912
940
}
913
941
942
+ private static int getNodeCount (JProgram jProgram ) {
943
+ Event countEvent = SpeedTracerLogger .start (CompilerEventType .OPTIMIZE , "phase" , "countNodes" );
944
+ TreeStatistics treeStats = new TreeStatistics ();
945
+ treeStats .accept (jProgram );
946
+ int numNodes = treeStats .getNodeCount ();
947
+ countEvent .end ();
948
+ return numNodes ;
949
+ }
950
+
914
951
private static MultipleDependencyGraphRecorder chooseDependencyRecorder (boolean soycEnabled ,
915
952
OutputStream out ) {
916
953
MultipleDependencyGraphRecorder dependencyRecorder = CodeSplitter .NULL_RECORDER ;
0 commit comments