mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[performance] different interner for path segments
+ implement simplest MRU-like interner, instead of Caffeine -- Caffeine seems to be an overkill for such a simple task. + Use `-Dcompiler.InternedPath.USE_CAFFEINE_INTERNER=true` to return Caffeine-based interner GitOrigin-RevId: 4b95e28dc2c4303d9078be445ee8f94e593ac176
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2264207f08
commit
7f76ca88d7
@@ -3,6 +3,7 @@ package com.intellij.compiler.server;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import it.unimi.dsi.fastutil.HashCommon;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -10,12 +11,21 @@ import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static com.intellij.util.SystemProperties.getBooleanProperty;
|
||||
|
||||
/**
|
||||
* Specialized data structure for compact in-memory storage of big amounts of path-like strings
|
||||
*/
|
||||
public abstract sealed class InternedPath permits InternedPath.WinInternedPath, InternedPath.UNCWinInternedPath, InternedPath.XInternedPath {
|
||||
private static final String PATH_SEPARATORS = '/' == File.separatorChar? File.separator : "/" + File.separator;
|
||||
private static final LoadingCache<String, String> ourNameCache = Caffeine.newBuilder().maximumSize(2048).build(key -> key);
|
||||
private static final String PATH_SEPARATORS = ('/' == File.separatorChar) ?
|
||||
File.separator :
|
||||
"/" + File.separator;
|
||||
|
||||
private static final boolean USE_CAFFEINE_INTERNER = getBooleanProperty("compiler.InternedPath.USE_CAFFEINE_INTERNER", false);
|
||||
|
||||
private static final PathSegmentsInterner ourNameCache = USE_CAFFEINE_INTERNER ?
|
||||
new CaffeineBasedInterner() :
|
||||
new SimplestMRUInterner();
|
||||
|
||||
protected final String[] myPath;
|
||||
|
||||
@@ -26,7 +36,7 @@ public abstract sealed class InternedPath permits InternedPath.WinInternedPath,
|
||||
List<String> list = new ArrayList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(path, PATH_SEPARATORS, false);
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
list.add(ourNameCache.get(tokenizer.nextToken()));
|
||||
list.add(ourNameCache.intern(tokenizer.nextToken()));
|
||||
}
|
||||
myPath = list.toArray(String[]::new);
|
||||
}
|
||||
@@ -36,13 +46,13 @@ public abstract sealed class InternedPath permits InternedPath.WinInternedPath,
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myPath.length > 0? myPath[myPath.length - 1] : "";
|
||||
return myPath.length > 0 ? myPath[myPath.length - 1] : "";
|
||||
}
|
||||
|
||||
public boolean contains(Predicate<String> pathElementMatcher) {
|
||||
return find(pathElementMatcher) != null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public String find(Predicate<String> pathElementMatcher) {
|
||||
for (String elem : myPath) {
|
||||
@@ -88,7 +98,9 @@ public abstract sealed class InternedPath permits InternedPath.WinInternedPath,
|
||||
}
|
||||
|
||||
public static InternedPath create(String path) {
|
||||
return path.startsWith("//") || path.startsWith("\\\\")? new UNCWinInternedPath(path.substring(2)) : path.startsWith("/")? new XInternedPath(path) : new WinInternedPath(path);
|
||||
return path.startsWith("//") || path.startsWith("\\\\")
|
||||
? new UNCWinInternedPath(path.substring(2))
|
||||
: path.startsWith("/") ? new XInternedPath(path) : new WinInternedPath(path);
|
||||
}
|
||||
|
||||
static final class WinInternedPath extends InternedPath {
|
||||
@@ -105,13 +117,13 @@ public abstract sealed class InternedPath permits InternedPath.WinInternedPath,
|
||||
if (myPath.length == 1) {
|
||||
String name = myPath[0];
|
||||
// handle the case of a Windows volume name
|
||||
return name.length() == 2 && name.endsWith(":")? name + "/" : name;
|
||||
return name.length() == 2 && name.endsWith(":") ? name + '/' : name;
|
||||
}
|
||||
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for (CharSequence element : myPath) {
|
||||
if (!buf.isEmpty()) {
|
||||
buf.append("/");
|
||||
buf.append('/');
|
||||
}
|
||||
buf.append(element);
|
||||
}
|
||||
@@ -131,9 +143,9 @@ public abstract sealed class InternedPath permits InternedPath.WinInternedPath,
|
||||
return "//";
|
||||
}
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
buf.append("/");
|
||||
buf.append('/');
|
||||
for (CharSequence element : myPath) {
|
||||
buf.append("/").append(element);
|
||||
buf.append('/').append(element);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
@@ -157,4 +169,50 @@ public abstract sealed class InternedPath permits InternedPath.WinInternedPath,
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
|
||||
private interface PathSegmentsInterner {
|
||||
String intern(@NotNull String segment);
|
||||
|
||||
void invalidateAll();
|
||||
}
|
||||
|
||||
/** String interner: very simple but very fast and suitable for concurrent environment */
|
||||
private static class SimplestMRUInterner implements PathSegmentsInterner {
|
||||
private static final int CACHE_SIZE = (1 << 12); // =4k
|
||||
|
||||
private final String[] cache = new String[CACHE_SIZE];
|
||||
|
||||
@Override
|
||||
public String intern(@NotNull String segment) {
|
||||
int index = HashCommon.mix(segment.hashCode()) & (CACHE_SIZE - 1);
|
||||
|
||||
String candidate = cache[index];
|
||||
if (segment.equals(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
cache[index] = segment;
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateAll(){
|
||||
Arrays.fill(cache, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CaffeineBasedInterner implements PathSegmentsInterner {
|
||||
private static final LoadingCache<@NotNull String, String> cache = Caffeine.newBuilder().maximumSize(2048).build(key -> key);
|
||||
|
||||
@Override
|
||||
public String intern(@NotNull String segment) {
|
||||
return cache.get(segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateAll() {
|
||||
cache.invalidateAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user