mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
less locking in FileNameCache (IDEA-142162)
This commit is contained in:
@@ -22,6 +22,8 @@ import com.intellij.util.io.PersistentStringEnumerator;
|
||||
import com.intellij.util.text.ByteArrayCharSequence;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
@@ -70,19 +72,48 @@ public class FileNameCache {
|
||||
return h % ourNameCache.length;
|
||||
}
|
||||
|
||||
private static final boolean ourTrackStats = false;
|
||||
private static final int ourLOneSize = 1024;
|
||||
private static final IntObjectLinkedMap.MapEntry<CharSequence>[] ourArrayCache = new IntObjectLinkedMap.MapEntry[ourLOneSize];
|
||||
|
||||
private static final AtomicInteger ourQueries = new AtomicInteger();
|
||||
private static final AtomicInteger ourMisses = new AtomicInteger();
|
||||
|
||||
@NotNull
|
||||
public static CharSequence getVFileName(int nameId) {
|
||||
assert nameId > 0;
|
||||
|
||||
if (ourTrackStats) {
|
||||
int frequency = 10000000;
|
||||
int queryCount = ourQueries.incrementAndGet();
|
||||
if (queryCount >= frequency && ourQueries.compareAndSet(queryCount, 0)) {
|
||||
double misses = ourMisses.getAndSet(0);
|
||||
//noinspection UseOfSystemOutOrSystemErr
|
||||
System.out.println("Misses: " + (misses / frequency));
|
||||
ourQueries.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
int l1 = nameId % ourLOneSize;
|
||||
IntObjectLinkedMap.MapEntry<CharSequence> entry = ourArrayCache[l1];
|
||||
if (entry != null && entry.key == nameId) {
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
if (ourTrackStats) {
|
||||
ourMisses.incrementAndGet();
|
||||
}
|
||||
|
||||
final int stripe = calcStripeIdFromNameId(nameId);
|
||||
IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>> cache = ourNameCache[stripe];
|
||||
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
||||
synchronized (cache) {
|
||||
IntObjectLinkedMap.MapEntry<CharSequence> entry = cache.getCachedEntry(nameId);
|
||||
if (entry != null) {
|
||||
return entry.value;
|
||||
}
|
||||
entry = cache.getCachedEntry(nameId);
|
||||
}
|
||||
|
||||
return cacheData(FSRecords.getNameByNameId(nameId), nameId, stripe).value;
|
||||
if (entry == null) {
|
||||
entry = cacheData(FSRecords.getNameByNameId(nameId), nameId, stripe);
|
||||
}
|
||||
ourArrayCache[l1] = entry;
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vfs.newvfs.impl;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.IdeaTestFixture;
|
||||
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TLongArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class FileNameCacheMicroBenchmark {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
//noinspection SSBasedInspection
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
IdeaTestFixture fixture = IdeaTestFixtureFactory.getFixtureFactory().createLightFixtureBuilder(LightProjectDescriptor.EMPTY_PROJECT_DESCRIPTOR).getFixture();
|
||||
fixture.setUp();
|
||||
|
||||
runTest(200);
|
||||
runTest(50000);
|
||||
|
||||
fixture.tearDown();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static void runTest(int nameCount) throws InterruptedException, ExecutionException {
|
||||
System.out.println("-----------");
|
||||
System.out.println("nameCount = " + nameCount);
|
||||
|
||||
TIntObjectHashMap<CharSequence> map = generateNames(nameCount);
|
||||
final int[] ids = map.keys();
|
||||
checkNames(map, ids);
|
||||
warmUp(ids);
|
||||
|
||||
measureAverageTime(ids, 1);
|
||||
measureAverageTime(ids, 3);
|
||||
}
|
||||
|
||||
private static boolean warmedUp;
|
||||
private static void warmUp(int[] ids) throws InterruptedException, ExecutionException {
|
||||
if (warmedUp) return;
|
||||
for (int i = 0; i < 200000; i++) {
|
||||
runThreads(ids, 2, 1000);
|
||||
}
|
||||
|
||||
Thread.sleep(10000);
|
||||
System.out.println("Warmup complete");
|
||||
warmedUp = true;
|
||||
}
|
||||
|
||||
private static void measureAverageTime(int[] ids, int threadCount) throws InterruptedException, ExecutionException {
|
||||
TLongArrayList times = new TLongArrayList();
|
||||
for (int i = 0; i < 11; i++) {
|
||||
long time = runThreads(ids, threadCount, 20000000);
|
||||
System.out.println(time);
|
||||
times.add(time);
|
||||
}
|
||||
|
||||
times.sort();
|
||||
System.out.println("Median for " + threadCount + " threads: " + times.get(times.size() / 2));
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private static long runThreads(final int[] ids, int threadCount, final int queryCount) throws InterruptedException, ExecutionException {
|
||||
long start = System.currentTimeMillis();
|
||||
List<Future<?>> futures = ContainerUtil.newArrayList();
|
||||
Random seedRandom = new Random();
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
final Random threadRandom = new Random(seedRandom.nextInt());
|
||||
futures.add(ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int j = 0; j < queryCount; j++) {
|
||||
FileNameCache.getVFileName(ids[threadRandom.nextInt(ids.length)]);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
for (Future<?> future : futures) {
|
||||
future.get();
|
||||
}
|
||||
return System.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
private static void checkNames(TIntObjectHashMap<CharSequence> map, int[] ids) {
|
||||
for (int id : ids) {
|
||||
Assert.assertEquals(map.get(id), FileNameCache.getVFileName(id).toString());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TIntObjectHashMap<CharSequence> generateNames(int nameCount) {
|
||||
Random random = new Random();
|
||||
TIntObjectHashMap<CharSequence> map = new TIntObjectHashMap<CharSequence>();
|
||||
for (int i = 0; i < nameCount; i++) {
|
||||
String name = "some_name_" + random.nextInt() + StringUtil.repeat("a", random.nextInt(10));
|
||||
int id = FileNameCache.storeName(name);
|
||||
map.put(id, name);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user