-
Notifications
You must be signed in to change notification settings - Fork 151
Description
During analysis of two heapdumps shared here the following issue was discovered as a hotspot that can benefit from optimization:
Performance Data
| Metric | WITH transitive | WITHOUT transitive | Ratio |
|---|---|---|---|
| Path.<init>() (µs) | 24,933,686 | 12,598,757 | 2.0× |
| Path.computeSegmentCount() (µs) | 12,651,521 | 3,382,879 | 3.7× |
| Path.append() (µs) | 14,264,596 | 5,157,314 | 2.8× |
| Path.equals() (µs) | 6,327,370 | 1,207,964 | 5.2× |
| StringLatin1.replace() (µs) | 32,550,291 | 9,422,718 | 3.5× |
Description
Path objects are created extensively throughout the JDT and Platform. The constructor calls backslashToForward() (which calls String.replace('\\', '/') — accounting for the StringLatin1.replace() overhead) and computeSegmentCount() (which scans the entire path string). The equals() method compares segments array-by-array.
With transitive dependencies, the number of classpath entries, package fragment roots, and resource lookups increases substantially, causing proportionally more Path creation and comparison. The 3.5× increase in StringLatin1.replace() (32.5 seconds total!) is particularly notable — this is pure overhead from the backslashToForward() conversion that happens in every Path constructor, even on Linux where no backslashes exist.
Suggested Fix
- Skip backslash conversion on non-Windows platforms:
Path(String)unconditionally callsbackslashToForward()which doespath.replace('\\', '/'). Since this build is running on a specific OS, the conversion could be skipped whenFile.separatorChar == '/'. (Note: The source already has aforWindowsparameter in the internal constructor, but the publicPath(String)usesConstants.RUNNING_ON_WINDOWS. Verify this is correctly set.) - Intern or cache frequently used paths: Classpath entry paths, project paths, and source folder paths are created repeatedly. An interning mechanism or flyweight pattern would reduce object creation.
- Lazy segment computation:
computeSegments()andcomputeSegmentCount()are called in the constructor. If manyPathobjects are created only for equality checks ortoString(), lazy computation would save work. - Use
IPath.of()factory method: If a canonicalIPath.of()exists (or should be added), it could return cached instances for common paths.