question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Class files compiled by this extention run faster than manually compiled with javac command.

See original GitHub issue

I was about to benchmark this program.

public class App {
    public static void main(String[] args) {
        int n_iters = Integer.parseInt(args[0]);
        long tInit = System.currentTimeMillis();
        int c = 0;

        for (int i = 0; i < n_iters; ++i) {
            for (int j = 0; j < n_iters; ++j) {
                for (int k = 0; k < n_iters; ++k) {
                    if (i * i + j * j == k * k) {
                        ++c;
                    }
                }
            }
        }
        System.out.println(c);
        System.out.println((System.currentTimeMillis() - tInit) / 1000.0);
    }
}

I found the class file compiled by VSCode Java extention runs about 4 times faster than one I compiled manually by the javac command.

Environment
  • Operating System: Windows 11 /Debian 11
  • JDK version: java 17.0.3.1
  • Visual Studio Code version: 1.71.2
  • Java extension version: v1.11.0
Steps To Reproduce
  1. clone git@github.com:lucidfrontier45/java_bench.git
  2. open the cloned directory with VSCode
  3. confirm VSCode has compiled bin/App.class
  4. manually compile src/App.java to src/App.class by javac src/App.java.
  5. run manually compiled one java -cp src App 1000
  6. run VSCode compiled one java -cp bin App 1000
  7. confirm 6 is faster than 5.

[Please attach a sample project reproducing the error] https://github.com/lucidfrontier45/java_bench

Current Result
PS C:\Users\lucid\workspace\java_bench> java -cp src App 1000
3755
1.946 sec
PS C:\Users\lucid\workspace\java_bench> java -cp bin App 1000
3755
0.564 sec
Expected Result

Both of them end in almost same duration.

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:1
  • Comments:6

github_iconTop GitHub Comments

1reaction
foraxcommented, Sep 24, 2022

Hi, this is how Java usually works, it interprets the code and if something is run multiple times, the VM asks the JIT to generate the corresponding assembly code and jump on it.

For Hotspot (the default OpenJDK VM), that optimization occurs either when a method is called several times (in the thousand) or a loop is called several times. The later optimization is called on stack replacement optimization.

Loop optimizations is a vast subject, but depending on the kind of loops, the JITs optimize them differently. I believe that the question here is, does the JIT is able to prove that the loop will finish fast enough so there is no need to insert a check to maybe trigger the GC when the loop run. If the JIT does not know, it has to insert such check (named a safepoint check).

For that, the JIT will try to prove that the loop finish, here, each loop is bounded and iterate over an int, usually it’s enough for the JIT to consider that the loop will finish soon enough.

It seems that depending on the bytecode generated either, the JIT struggles to detect the kind of loops or there is a bad interaction between the loop detection and the on stack replacement mechanism, but clearly there is a bug somewhere.

I suggest you to report that as a bug of the hotspot compiler (the JIT).

1reaction
lucidfrontier45commented, Sep 24, 2022

@tivrfoa @fbricon

The benchmark program I posted was taken from https://stackoverflow.com/questions/67211077/java-vs-rust-performance .

Since this program is mostly composed by primitive variables without any complex memory allocation, I expected it to be as fast as C or Rust. In fact, the class file compiled with ecj runs as fast as C and Rust. I’m not sure why javac cannot do this.

There is an explanation (though I couldn’t understand.)

I can reproduce this when comparing ecj compiled code with javac compiled code. You are right that there are no optimizations happening at compile time. There’s just a subtle difference in the loop compilation, one is combining a conditional forward branch with an unconditional backward branch whereas the other is combining an unconditional forward branch with a conditional backward branch. Shouldn’t make a difference when the runtime optimizer is fully working, but having the entire program in the main method hinders optimizations.

https://stackoverflow.com/questions/73814988/why-is-java-program-compiled-by-vscode-faster-than-one-created-by-manually-runni#comment130342732_73814988

I also found that in the triple ijk loop, if I extract jk loop as an function, the JVM JIT can optimize it and it runs faster, close to C and Rust.

public class App {
    public static void main(String[] args) {
        int n_iters = Integer.parseInt(args[0]);
        long tInit = System.currentTimeMillis();
        int c = 0;
        for (int i = 0; i < n_iters; i++) {
            c += inner_loop(i, n_iters);
        }
        System.out.println(c);
        System.out.println((System.currentTimeMillis() - tInit) / 1000.0);
    }

    public static int inner_loop(int i, int n_iters) {
        int c = 0;

        for (int j = 0; j < n_iters; ++j) {
            for (int k = 0; k < n_iters; ++k) {
                if (i * i + j * j == k * k) {
                    ++c;
                }
            }
        }

        return c;
    }
}
PS C:\Users\lucid\workspace\java_bench> java -cp src App 1000
3755
0.583
Read more comments on GitHub >

github_iconTop Results From Across the Web

terminal not overwriting .class files - java - Stack Overflow
for the class i have to use jEdit to write programs, and terminal to compile and run them (i have a mac, and...
Read more >
Compiling Java *.class Files with javac - Baeldung
This tutorial will introduce the javac tool and describes how to use it to compile Java source files into class files.
Read more >
Chapter 1. Getting Started: Compiling and Running Java
Use the commands javac to compile and java to run your program (and, ... and a compiled .class file is not, javac will...
Read more >
The javac Command - Oracle Help Center
The javac command reads source files that contain module, package and type declarations written in the Java programming language, and compiles them into...
Read more >
Javac Task - Apache Ant
The source and destination directory will be recursively scanned for Java source files to compile. Only .java files that have no corresponding .class...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found