Performance of variable argument methods in Java - java
I have this question about the performance of a method in Java with a variable number of parameters.
Say I have the following 2 alternatives:
public static final boolean isIn(int i, int v1, int v2) {
return (v1 == i) || (v2 == i);
}
public static final boolean isIn(int i, int... values) {
for (int v : values) {
if (i == v) {
return true;
}
}
return false;
}
Now the main problem comes when I have versions of the first method that go up to 20, 30 or even 50 parameters. Now that just hurts the eyes. Ok, this is legacy code and I'd like to replace all of it with the only one variable arguments method.
Any idea what the impact on performance would be? Any chance the compiler does some optimization for the second method so that it resembles the first form more or less?
EDIT: Ok, maybe I was not clear enough. I don't have performance problems with the methods with 50 arguments. It's just about readability as Peter Lawrey said.
I was wondering about performance problems if I switch to the new method with variable number of arguments.
In other words: what would be the best way to do it if you care about performance? Methods with 50 arguments or the only one method with variable arguments?
I had the same question, and turned to experimentation.
public class ArgTest {
int summation(int a, int b, int c, int d, int e, int f) {
return a + b + c + d + e + f;
}
int summationVArgs(int... args) {
int sum = 0;
for (int arg : args) {
sum += arg;
}
return sum;
}
final static public int META_ITERATIONS = 200;
final static public int ITERATIONS = 1000000;
static public void main(String[] args) {
final ArgTest at = new ArgTest();
for (int loop = 0; loop < META_ITERATIONS; loop++) {
int sum = 0;
final long fixedStart = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
sum += at.summation(2312, 45569, -9816, 19122, 4991, 901776);
}
final long fixedEnd = System.currentTimeMillis();
final long vargStart = fixedEnd;
for (int i = 0; i < ITERATIONS; i++) {
sum += at.summationVArgs(2312, 45569, -9816, 19122, 4991, 901776);
}
final long vargEnd = System.currentTimeMillis();
System.out.printf("%03d:%d Fixed-Args: %d ms\n", loop+1, ITERATIONS, fixedEnd - fixedStart);
System.out.printf("%03d:%d Vargs-Args: %d ms\n", loop+1, ITERATIONS, vargEnd - vargStart);
}
System.exit(0);
}
}
If you run this code on a modern JVM (here 1.8.0_20), you will see that the variable number of arguments cause overhead in performance and possible in memory consumption as well.
I'll only post the first 25 runs:
001:1000000 Fixed-Args: 16 ms
001:1000000 Vargs-Args: 45 ms
002:1000000 Fixed-Args: 13 ms
002:1000000 Vargs-Args: 32 ms
003:1000000 Fixed-Args: 0 ms
003:1000000 Vargs-Args: 27 ms
004:1000000 Fixed-Args: 0 ms
004:1000000 Vargs-Args: 22 ms
005:1000000 Fixed-Args: 0 ms
005:1000000 Vargs-Args: 38 ms
006:1000000 Fixed-Args: 0 ms
006:1000000 Vargs-Args: 11 ms
007:1000000 Fixed-Args: 0 ms
007:1000000 Vargs-Args: 17 ms
008:1000000 Fixed-Args: 0 ms
008:1000000 Vargs-Args: 40 ms
009:1000000 Fixed-Args: 0 ms
009:1000000 Vargs-Args: 89 ms
010:1000000 Fixed-Args: 0 ms
010:1000000 Vargs-Args: 21 ms
011:1000000 Fixed-Args: 0 ms
011:1000000 Vargs-Args: 16 ms
012:1000000 Fixed-Args: 0 ms
012:1000000 Vargs-Args: 26 ms
013:1000000 Fixed-Args: 0 ms
013:1000000 Vargs-Args: 7 ms
014:1000000 Fixed-Args: 0 ms
014:1000000 Vargs-Args: 7 ms
015:1000000 Fixed-Args: 0 ms
015:1000000 Vargs-Args: 6 ms
016:1000000 Fixed-Args: 0 ms
016:1000000 Vargs-Args: 141 ms
017:1000000 Fixed-Args: 0 ms
017:1000000 Vargs-Args: 139 ms
018:1000000 Fixed-Args: 0 ms
018:1000000 Vargs-Args: 106 ms
019:1000000 Fixed-Args: 0 ms
019:1000000 Vargs-Args: 70 ms
020:1000000 Fixed-Args: 0 ms
020:1000000 Vargs-Args: 6 ms
021:1000000 Fixed-Args: 0 ms
021:1000000 Vargs-Args: 5 ms
022:1000000 Fixed-Args: 0 ms
022:1000000 Vargs-Args: 6 ms
023:1000000 Fixed-Args: 0 ms
023:1000000 Vargs-Args: 12 ms
024:1000000 Fixed-Args: 0 ms
024:1000000 Vargs-Args: 37 ms
025:1000000 Fixed-Args: 0 ms
025:1000000 Vargs-Args: 12 ms
...
Even at the best of times, the Vargs-Args never dropped to 0ms.
The compiler does next to no optimisation. The JVM can optimise code but the two methods won't perform anything like each other. If you have lines of code like isIn(i, 1,2,3,4,5,6,7,8,9 /* plus 40 more */) you have more than performance issues to worry about IMHO. I would worry about readability first.
If you are worried about performance pass the arguments as a int[] which is reused.
BTW The most efficient way to look up a large set of int values is to use a Set like TIntHashSet
to #Canonical Chris
I don't think problem at your test come from variable argument. The function sumationVArgs take more time to complete because of for loop.
I created this function and added to the benchmark
int summationVArgs2(int... args) {
return args[0] + args[1] + args[2] + args[3] + args[4] + args[5];
}
and this is what I see:
028:1000000 Fixed-Args: 0 ms
028:1000000 Vargs-Args: 12 ms
028:1000000 Vargs2-Args2: 0 ms
The for loop in function "summationVArgs" is compiled to more operations than add function. It contains add operation to increase iterator, check operation to check condition and branch operation to loop and exit loop, and all of them execute once for each loop except branch opration to exit loop.
Sorry for my bad English. I hop you can understand my English :)
Come back when you have profiler output that says this is a problem. Until then, it's premature optimization.
It will be the same as if you declared
isIn(int i, int[] values) {
However there will be some some small overhead in packaging the variables up when calling your method
Heard about the two optimisation rules:
Don't optimize
(For experts only!) Don't optimize yet
In other words this is nothing you should care about from the performance point of view.
Related
How to get time of Fibonacci recursion
I have an task, they give to me an recursion fibonacci algoritm and ask to return a time. I done it with .SystemCurrentMilis and i post my code below, but the testers said that its takes to long to count time, so i think i must give them some teoretic time, because execute function and get time is too long. Hope on yours help guys. How to make function timetocompute work faster on whatever n coming. import java.math.BigInteger; import java.math.BigDecimal; public class Fibonacci { public BigDecimal timeToComputeRecursiveFibonacci(int n) { long startTime = System.currentTimeMillis(); recursive(n); long finishTime = System.currentTimeMillis(); long tempTime = finishTime - startTime; BigDecimal usedTime = BigDecimal.valueOf(tempTime); return usedTime; } public BigInteger recursive(int n) { if (n <= 1) return BigInteger.valueOf(n); return recursive(n - 1).add(recursive(n - 2)); } }
I have created some measurements for the recursive function. If you think about it, if you can measure recursive(n) then recursive(n+1) would be almost 2 times slower, as it needs to calculate the function for n, and n-1 as well. nth - ms 30 - 25 31 - 43 32 - 70 33 - 113 34 - 196 35 - 301 36 - 513 37 - 926 38 - 1266 39 - 2210 40 - 3652 So you can estimate 40th number from the 30th time with 1.65^10*25, which is 1.65^m*time(n). This can be converted to years. I would try to do something like this.
Convert a double list to a grouped string
The program has an input a list of doubles and the output needs to be a string containing the list values grouped by their value. The list values will be grouped if they are equal. Something like: input 9,77,5,5,31 => output 9 77 2*5 31 I created an algorithm in C# (in Java I think that is almost the same) for this but I am not sure if it can be improved regarding its speed or code quaility, or if it has some bugs that I could not see. The algorithm having also some more input, output examples is below. List<double> input = new List<double> { 11, 32, 32, 43}; // output 11 2*32 43 //List<double> input = new List<double> { 11, 11, 43, 43 }; // output 2*11 2*43 //List<double> input = new List<double> { 10, 11, 12, 13, 14, 15, 16 }; // output 10 11 12 13 14 15 16 //List<double> input = new List<double> { 11, 11, 11, 11, 11 }; // output 5 * 11 //List<double> input = new List<double> { 11, 11, 32, 22, 22, 22, 4, 10, 10 }; // output 2*11 32 3*22 4 2*10 string listAsString = string.Empty; double nextElem = double.MinValue; for (int i = 0; i < input.Count; i++) { double currentElem = input[i]; if (i + 1 < input.Count) { nextElem = input[i + 1]; } int equalCount = 0; while (currentElem.Equals(nextElem) && i < input.Count) { equalCount++; i++; currentElem = nextElem; if (i < input.Count) { nextElem = input[i]; } } if (equalCount < 2) { listAsString += currentElem + " "; } else { listAsString += equalCount + "*" + currentElem + " "; i--; } } Console.WriteLine(listAsString); Please let me know if you noticed some bugs or see some improvements that can be done. Also if you know another implementation of this requirement please add it so that a comparation regarding results, speed, code quality between the algorithms can be done... and find the best way to handle this.
Since the requirement is to group only consecutive equal values, the Dictionary and LINQ GroupBy approaches mentioned in another answer do not apply because they will produce incorrect result for input sequence like 1,2,1. Also there is no standard LINQ method for doing such grouping (except eventually Aggregate method, but it's no more than inefficient for / foreach loop equivalent). Shortly, your algorithm is the best for such task. But implementation is not. The main bottleneck is the string concatenation as mentioned by Peroxy, which (also mentioned in the other answer) is easily fixable by utilizing the StringBuilder class. Once you do that, the performance will be just fine. The other issue I see in the implementation is usage of special values (double.MinValue), duplicate corner case checks, decrementing for loop variable inside the body etc. So although it probably works and I don't see directly a bug, it's kind of hard to follow the algorithm logic and spot a potential bug just reading the implementation. The algorithm itself is quite simple, I would implement it this way: static string ListAsString(List<double> input) { var sb = new StringBuilder(); for (int i = 0; i < input.Count; ) { var value = input[i]; int count = 1; while (++i < input.Count && input[i] == value) count++; if (sb.Length > 0) sb.Append(' '); if (count > 1) sb.Append(count).Append('*'); sb.Append(value); } return sb.ToString(); } which IMO is quite easier to follow. Note that there is no duplicate code, no special values and the loop variable i advancing is done only in one place inside the outer loop body. Again, this has nothing to do with performance (which is provided by the StringBuilder usage), but simply readability, redundancy elimination and less error prone.
Personally, I see great potential with Dictionary usage here, here is a quick solution I made with a dictionary implementation: var input = new List<double> { 9, 77, 5, 5, 31 }; var dict = new Dictionary<double, int>(); var listAsString = new StringBuilder(); foreach (var item in input) { if (dict.ContainsKey(item)) dict[item]++; else dict[item] = 1; } foreach (var item in dict) { listAsString.Append(item.Value > 1 ? $"{item.Value}*{item.Key} " : $"{item.Key} "); } Console.WriteLine(listAsString); If you ever wanted a non efficient LINQ one liner solution: string result = string.Join(" ", input.GroupBy(i => i) .Select(x => x.Count() > 1 ? $"{x.Count()}*{x.Key} " : $"{x.Key} ")); However, I believe your method is written nicely, albeit a bit less readable than the dictionary one, but the main flaw with your solution is that you are using a string when building the final string, you should definitely be using a StringBuilder, I have introduced the StringBuilder in your method and made comparisons between these three methods: Dictionary | Your method | GroupBy method ------------------------------------------------ 2 ms | 0 ms | 5 ms n=3 0 ms | 0 ms | 0 ms n=6 0 ms | 0 ms | 0 ms n=12 0 ms | 0 ms | 0 ms n=24 0 ms | 0 ms | 0 ms n=48 0 ms | 0 ms | 0 ms n=96 0 ms | 0 ms | 0 ms n=192 0 ms | 0 ms | 0 ms n=384 0 ms | 0 ms | 0 ms n=768 0 ms | 0 ms | 0 ms n=1536 1 ms | 0 ms | 1 ms n=3072 3 ms | 2 ms | 3 ms n=6144 5 ms | 4 ms | 6 ms n=12288 8 ms | 7 ms | 14 ms n=24576 14 ms | 13 ms | 25 ms n=49152 31 ms | 32 ms | 66 ms n=98304 80 ms | 59 ms | 146 ms n=196608 149 ms | 123 ms | 294 ms n=393216 246 ms | 218 ms | 504 ms n=786432 483 ms | 428 ms | 1040 ms n=1572864 999 ms | 873 ms | 2070 ms n=3145728 1995 ms | 1784 ms | 3950 ms n=6291456 Your solution is always the fastest, if you want to go for speed, keep your solution, but change it to use StringBuilder, use listAsString.Append(currentElem + " ") instead of listAsString += currentElem + " ". GroupBy could be used if you will only operate with collections that have n < 1000, use the Dictionary solution if you would rather settle with readability over speed.
Memory error in java
I am trying to code in java prime number algorithm. I am trying to face the problems with the integer and long size. My simply code is the following which works until a limit for n: public static long f(int n) { if (n == 1 || n == 2) return 1; else { long value = f(n-2) + f(n-1); return value; } } If in my main I ll give 50 for example my code will be crushed, I am guessing due to the size of the outcome. I ve got also another approach which I am struggle to understand it, which is the following: private static long[] cache = new long[60]; public static long f(int n) { if (n == 1 || n == 2) return 1; else if (cache[n] > 0) return cache[n]; else { long value = f(n-2) + f(n-1); cache[n] = value; return value; } } With this approach everything works fine whatever is the n, my issue is that I cannot get the difference.
By "crushed" you mean that the computation runs very long. The reason is that the same call is made many times. If you add this to your method: static long count; public static long f(int n) { count++; ... you'll see how many times the method is executed. For f(50), it is actually calling the method 25,172,538,049 times, which runs in 41 seconds in my machine. When you cache the result of previous invocations, called memoization, you eliminate all the redundant calls, e.g. f(40) = f(39) + f(38), but f(39) = f(38) + f(37), so f(38) is called twice. Remembering the result of f(38) means that subsequent invocation has the answer immediately, without having to redo the recursion. Without memoization, I get this: n f(n) count time(ns) == ============== =============== ============== 1 1 1 6,273 2 1 1 571 3 2 3 855 4 3 5 1,141 5 5 9 1,425 6 8 15 1,140 7 13 25 1,996 8 21 41 2,851 9 34 67 7,413 10 55 109 16,536 11 89 177 8,839 12 144 287 19,103 13 233 465 21,098 14 377 753 11,405 15 610 1,219 5,703 16 987 1,973 9,979 17 1,597 3,193 21,099 18 2,584 5,167 32,788 19 4,181 8,361 35,639 20 6,765 13,529 57,307 21 10,946 21,891 91,521 22 17,711 35,421 147,687 23 28,657 57,313 237,496 24 46,368 92,735 283,970 25 75,025 150,049 331,583 26 121,393 242,785 401,720 27 196,418 392,835 650,052 28 317,811 635,621 1,053,483 29 514,229 1,028,457 1,702,679 30 832,040 1,664,079 2,750,745 31 1,346,269 2,692,537 4,455,137 32 2,178,309 4,356,617 12,706,520 33 3,524,578 7,049,155 11,714,051 34 5,702,887 11,405,773 19,571,980 35 9,227,465 18,454,929 30,605,757 36 14,930,352 29,860,703 51,298,507 37 24,157,817 48,315,633 84,473,965 38 39,088,169 78,176,337 127,818,746 39 63,245,986 126,491,971 208,727,118 40 102,334,155 204,668,309 336,785,071 41 165,580,141 331,160,281 543,006,638 42 267,914,296 535,828,591 875,782,771 43 433,494,437 866,988,873 1,429,555,753 44 701,408,733 1,402,817,465 2,301,577,345 45 1,134,903,170 2,269,806,339 3,724,691,882 46 1,836,311,903 3,672,623,805 6,010,675,962 47 2,971,215,073 5,942,430,145 9,706,561,705 48 4,807,526,976 9,615,053,951 15,715,064,841 49 7,778,742,049 15,557,484,097 25,427,015,418 50 12,586,269,025 25,172,538,049 41,126,559,697
If you get a StackOverflowError, it is due to the recursive nature of your algorithm. The second algorithm stores known results into an array to prevent functions piling up when it asks for an already computed result.
One of the problem can be the big number result, which is more than integer limit: fibonacci for 50 = 12,586,269,025 2,147,483,647 - int max and the other can be due to the recursion stackoverflow like #Xalamadrax pointed.
How much memory I'll get If I'll run my code?
What size of memory I will get if I will run this code? List<Long> list = new LinkedList<>(); for (long i = 0; i < 4_000_000_000L; i++) { list.add(i); } 8Gb?
Such questions can be answered using the Java Object Layout tool. Here's the test code: import org.openjdk.jol.info.GraphLayout; import org.openjdk.jol.util.VMSupport; import java.util.*; public class JOLTest { public static void main(String[] args) throws Exception { List<Long> list = new LinkedList<>(); for (long i = 0; i < 4000L; i++) { list.add(i); if((i+1) % 1000 == 0) { System.out.println("i = "+(i+1)); System.out.println(GraphLayout.parseInstance(list).toFootprint()); } } } } I have not enough memory to check 4_000_000_000 entries, but it's not necessary as LinkedList memory allocation is linear: i = 1000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 1000 24 24000 java.lang.Long 1 32 32 java.util.LinkedList 1000 24 24000 java.util.LinkedList$Node 2001 48032 (total) i = 2000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 2000 24 48000 java.lang.Long 1 32 32 java.util.LinkedList 2000 24 48000 java.util.LinkedList$Node 4001 96032 (total) i = 3000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 3000 24 72000 java.lang.Long 1 32 32 java.util.LinkedList 3000 24 72000 java.util.LinkedList$Node 6001 144032 (total) i = 4000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 4000 24 96000 java.lang.Long 1 32 32 java.util.LinkedList 4000 24 96000 java.util.LinkedList$Node 8001 192032 (total) So you practically have (48*n+32) bytes spent for n elements in the list, of which 24*n is Long instance, 24*n is internal LinkedList$Node instance and 32 is LinkedList itself. Thus the answer on your question would be 192_000_000_032 bytes... But this test was performed for small (<32Gb) -Xmx value when compressed oops work. For bigger amounts the numbers are different: $ java -Xmx32G -cp test-1.0.jar JOLTest i = 1000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 1000 24 24000 java.lang.Long 1 48 48 java.util.LinkedList 1000 40 40000 java.util.LinkedList$Node 2001 64048 (total) i = 2000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 2000 24 48000 java.lang.Long 1 48 48 java.util.LinkedList 2000 40 80000 java.util.LinkedList$Node 4001 128048 (total) i = 3000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 3000 24 72000 java.lang.Long 1 48 48 java.util.LinkedList 3000 40 120000 java.util.LinkedList$Node 6001 192048 (total) i = 4000 java.util.LinkedList#41a4555ed footprint: COUNT AVG SUM DESCRIPTION 4000 24 96000 java.lang.Long 1 48 48 java.util.LinkedList 4000 40 160000 java.util.LinkedList$Node 8001 256048 (total) Thus in your case it's actually (64*n+48) bytes, namely 256_000_000_048 (roughly 256Gb). Note that the results were obtained on Oracle JDK 1.8.0_40. It's not guaranteed that different JDK/JVM will produce the same result. Also please note that normally Java collections cannot fit more than Integer.MAX_VALUE elements as size is stored in int. Looking into LinkedList implementation you can see that (assuming that you have enough memory) you will get no exception, but the size will silently overflow, thus probably it would be hard to work with this collection after that.
Understanding the output of -XX:+PrintCompilation
I am running some micro benchmarks on Java list iteration code. I have used -XX:+PrintCompilation, and -verbose:gc flags to ensure that nothing is happening in the background when the timing is being run. However, I see something in the output which I cannot understand. Here's the code, I am running the benchmark on: import java.util.ArrayList; import java.util.List; public class PerformantIteration { private static int theSum = 0; public static void main(String[] args) { System.out.println("Starting microbenchmark on iterating over collections with a call to size() in each iteration"); List<Integer> nums = new ArrayList<Integer>(); for(int i=0; i<50000; i++) { nums.add(i); } System.out.println("Warming up ..."); //warmup... make sure all JIT comliling is done before the actual benchmarking starts for(int i=0; i<10; i++) { iterateWithConstantSize(nums); iterateWithDynamicSize(nums); } //actual System.out.println("Starting the actual test"); long constantSizeBenchmark = iterateWithConstantSize(nums); long dynamicSizeBenchmark = iterateWithDynamicSize(nums); System.out.println("Test completed... printing results"); System.out.println("constantSizeBenchmark : " + constantSizeBenchmark); System.out.println("dynamicSizeBenchmark : " + dynamicSizeBenchmark); System.out.println("dynamicSizeBenchmark/constantSizeBenchmark : " + ((double)dynamicSizeBenchmark/(double)constantSizeBenchmark)); } private static long iterateWithDynamicSize(List<Integer> nums) { int sum=0; long start = System.nanoTime(); for(int i=0; i<nums.size(); i++) { // appear to do something useful sum += nums.get(i); } long end = System.nanoTime(); setSum(sum); return end-start; } private static long iterateWithConstantSize(List<Integer> nums) { int count = nums.size(); int sum=0; long start = System.nanoTime(); for(int i=0; i<count; i++) { // appear to do something useful sum += nums.get(i); } long end = System.nanoTime(); setSum(sum); return end-start; } // invocations to this method simply exist to fool the VM into thinking that we are doing something useful in the loop private static void setSum(int sum) { theSum = sum; } } Here's the output. 152 1 java.lang.String::charAt (33 bytes) 160 2 java.lang.String::indexOf (151 bytes) 165 3Starting microbenchmark on iterating over collections with a call to size() in each iteration java.lang.String::hashCode (60 bytes) 171 4 sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes) 183 5 java.lang.String::lastIndexOf (156 bytes) 197 6 java.io.UnixFileSystem::normalize (75 bytes) 200 7 java.lang.Object::<init> (1 bytes) 205 8 java.lang.Number::<init> (5 bytes) 206 9 java.lang.Integer::<init> (10 bytes) 211 10 java.util.ArrayList::add (29 bytes) 211 11 java.util.ArrayList::ensureCapacity (58 bytes) 217 12 java.lang.Integer::valueOf (35 bytes) 221 1% performance.api.PerformantIteration::main # 21 (173 bytes) Warming up ... 252 13 java.util.ArrayList::get (11 bytes) 252 14 java.util.ArrayList::rangeCheck (22 bytes) 253 15 java.util.ArrayList::elementData (7 bytes) 260 2% performance.api.PerformantIteration::iterateWithConstantSize # 19 (59 bytes) 268 3% performance.api.PerformantIteration::iterateWithDynamicSize # 12 (57 bytes) 272 16 performance.api.PerformantIteration::iterateWithConstantSize (59 bytes) 278 17 performance.api.PerformantIteration::iterateWithDynamicSize (57 bytes) Starting the actual test Test completed... printing results constantSizeBenchmark : 301688 dynamicSizeBenchmark : 782602 dynamicSizeBenchmark/constantSizeBenchmark : 2.5940773249184588 I don't understand these four lines from the output. 260 2% performance.api.PerformantIteration::iterateWithConstantSize # 19 (59 bytes) 268 3% performance.api.PerformantIteration::iterateWithDynamicSize # 12 (57 bytes) 272 16 performance.api.PerformantIteration::iterateWithConstantSize (59 bytes) 278 17 performance.api.PerformantIteration::iterateWithDynamicSize (57 bytes) Why are both these methods being compiled twice ? How do I read this output... what do the various numbers mean ?
I am going to attempt answering my own question with the help of this link posted by Thomas Jungblut. 260 2% performance.api.PerformantIteration::iterateWithConstantSize # 19 (59 bytes) 268 3% performance.api.PerformantIteration::iterateWithDynamicSize # 12 (57 bytes) 272 16 performance.api.PerformantIteration::iterateWithConstantSize (59 bytes) 278 17 performance.api.PerformantIteration::iterateWithDynamicSize (57 bytes) First column The first column '260' is the timestamp. Second column The second column is the compilation_id and method_attributes. When a HotSpot compilation is triggered, every compilation unit gets a compilation id. The number in the second column is the compilation id. JIT compilation, and OSR compilation have two different sequences for the compilation id. So 1% and 1 are different compilation units. The % in the first two rows, refer to the fact that this is an OSR compilation. An OSR compilation was triggered because the code was looping over a large loop, and the VM determined that this code is hot. So an OSR compilation was triggered, which would enable the VM to do an On Stack Replacement and move over to the optimized code, once it is ready. Third column The third column performance.api.PerformantIteration::iterateWithConstantSize is the method name. Fourth column The fourth column is again different when OSR compilation happens and when it does not. Let's look at the common parts first. The end of the fourth column (59 bytes), refers to the size of the compilation unit in bytecode (not the size of the compiled code). The # 19 part in OSR compilation refers to the osr_bci. I am going to quote from the link mentioned above - A "place" in a Java method is defined by its bytecode index (BCI), and the place that triggered an OSR compilation is called the "osr_bci". An OSR-compiled nmethod can only be entered from its osr_bci; there can be multiple OSR-compiled versions of the same method at the same time, as long as their osr_bci differ. Finally, why was the method compiled twice ? The first one is an OSR compilation, which presumably happened while the loop was running due to the warmup code (in the example), and the second compilation is a JIT compilation, presumably to further optimize the compiled code ?
I think first time OSR happened , then it change the Invocation Counter tigger method compilar (PS: sorry, my english is pool)