JDK-8258036 : Significant performance regression in File.getCanonicalFile, getCanonicalPath
  • Type: Bug
  • Component: performance
  • Sub-Component: libraries
  • Affected Version: 12,16
  • Priority: P3
  • Status: Resolved
  • Resolution: Won't Fix
  • OS: generic
  • CPU: generic
  • Submitted: 2020-11-21
  • Updated: 2020-12-15
  • Resolved: 2020-12-14
Related Reports
Relates :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
Regression observed with

Ubuntu 20.04 with Java 12, Java 16 early access build 25 (both from jdk.java.net)
Windows 10 with Java 12, Java 16 early access build 25  (both from jdk.java.net)
macOS 10.15.7 with Java 15, Java 16 early access build 25  (both from jdk.java.net)

A DESCRIPTION OF THE PROBLEM :
File.getCanonicalFile and getCanonicalPath seem to have regressed significantly in Java 12 compared to Java 11.
I ran a small "benchmark" on Linux, macOS and Windows. The results are:

Linux Java 11: 1027ms, Java 12: 11559ms
macOS Java 11: 1035ms, Java 15: 112198ms
Windows Java 11: 1245ms, Java 12: 525467ms

The numbers with Java 16 early access build 25 are similar to Java 12.
I used reference implementations from jdk.java.net for all the tests.

As you can see, the performance regression ranges from 10x to 50x (Windows). For code that process many paths, this can be a major bottleneck, which wasn't significant before.

REGRESSION : Last worked in version 11.0.9

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code snippet included in this report, compare Java 11 to newer versions.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
File.getCanonicalFile, getCanonicalPath performance should not regress significantly.
ACTUAL -
File.getCanonicalFile, getCanonicalPath are 10-50 times slower, depending on the OS.

---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.IOException;

public class Test {

	public static void main(String[] args) {
		long oldTime = System.currentTimeMillis();
		try {
			for (int i = 0; i < 10000000; i++) {				
				new File("/tmp/../").getCanonicalFile();
			}
		} catch (IOException e) {
		}
		long newTime = System.currentTimeMillis() - oldTime;
		System.out.println(newTime);
	}
}

---------- END SOURCE ----------

FREQUENCY : always



Comments
Use the suggested -D property to use the cache as before 15-b3.
14-12-2020

This is due to JDK-8207005. Use -Dsun.io.useCanonCaches=true to revert to the earlier behavior.
14-12-2020

The execution time for the provided test case is longer from JDK 12(build 3) and onwards. OS: Windows JDK 11.0.9 : Pass | Output:1847ms JDK 12.0.2 : Pass | Output:1775ms JDK 12.0.3 : Fail | Output: 653524ms JDK 16ea27: Fail | Output: 573828ms Moving it to dev team for further analysis.
10-12-2020