mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Reverted
This commit is contained in:
+82
-13
@@ -16,35 +16,104 @@
|
||||
package com.intellij.openapi.vfs.newvfs.impl;
|
||||
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.FSRecords;
|
||||
import com.intellij.util.containers.LRUConcurrentIntObjectMap;
|
||||
import com.intellij.util.IntSLRUCache;
|
||||
import com.intellij.util.containers.IntObjectLinkedMap;
|
||||
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
|
||||
*/
|
||||
public class FileNameCache {
|
||||
private static final LRUConcurrentIntObjectMap<CharSequence> ourNameCache = new LRUConcurrentIntObjectMap<CharSequence>(65000);
|
||||
private static final PersistentStringEnumerator ourNames = FSRecords.getNames();
|
||||
@SuppressWarnings("unchecked") private static final IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>>[] ourNameCache = new IntSLRUCache[16];
|
||||
static {
|
||||
final int protectedSize = 40000 / ourNameCache.length;
|
||||
final int probationalSize = 20000 / ourNameCache.length;
|
||||
for(int i = 0; i < ourNameCache.length; ++i) {
|
||||
ourNameCache[i] = new IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>>(protectedSize, probationalSize);
|
||||
}
|
||||
}
|
||||
|
||||
public static int storeName(@NotNull String name) {
|
||||
final int idx = FSRecords.getNameId(name);
|
||||
CharSequence rawName = ByteArrayCharSequence.convertToBytesIfAsciiString(name);
|
||||
ourNameCache.put(idx, rawName);
|
||||
cacheData(name, idx, calcStripeIdFromNameId(idx));
|
||||
return idx;
|
||||
}
|
||||
|
||||
private static final LRUConcurrentIntObjectMap.IntFunction<CharSequence> getNameById = new LRUConcurrentIntObjectMap.IntFunction<CharSequence>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CharSequence apply(int id) {
|
||||
String name = FSRecords.getNameByNameId(id);
|
||||
return ByteArrayCharSequence.convertToBytesIfAsciiString(name);
|
||||
@NotNull
|
||||
private static IntObjectLinkedMap.MapEntry<CharSequence> cacheData(String name, int id, int stripe) {
|
||||
if (name == null) {
|
||||
ourNames.markCorrupted();
|
||||
throw new RuntimeException("VFS name enumerator corrupted");
|
||||
}
|
||||
};
|
||||
|
||||
CharSequence rawName = ByteArrayCharSequence.convertToBytesIfAsciiString(name);
|
||||
IntObjectLinkedMap.MapEntry<CharSequence> entry = new IntObjectLinkedMap.MapEntry<CharSequence>(id, rawName);
|
||||
IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>> cache = ourNameCache[stripe];
|
||||
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
||||
synchronized (cache) {
|
||||
return cache.cacheEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private static int calcStripeIdFromNameId(int id) {
|
||||
int h = id;
|
||||
h -= h<<6;
|
||||
h ^= h>>17;
|
||||
h -= h<<9;
|
||||
h ^= h<<4;
|
||||
h -= h<<3;
|
||||
h ^= h<<10;
|
||||
h ^= h>>15;
|
||||
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 : nameId;
|
||||
return ourNameCache.computeIfAbsent(nameId, getNameById);
|
||||
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) {
|
||||
entry = cache.getCachedEntry(nameId);
|
||||
}
|
||||
if (entry == null) {
|
||||
entry = cacheData(FSRecords.getNameByNameId(nameId), nameId, stripe);
|
||||
}
|
||||
ourArrayCache[l1] = entry;
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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.util.containers;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
|
||||
/**
|
||||
* A queue holding {@code int} values which has fixed capacity.
|
||||
* When a value is pushed (via {@link #push} method) and the queue overflows (i.e. the size becomes greater than capacity),
|
||||
* the LRU value (i.e. the value which was pushed a {@code capacity} pushes ago) is removed from the queue and returned as a method result.
|
||||
* Thread safe.
|
||||
*/
|
||||
public class FixedConcurrentIntQueue {
|
||||
private final AtomicInteger tail = new AtomicInteger();
|
||||
final AtomicIntegerArray queue;
|
||||
private final int capacity;
|
||||
private final int tombValue;
|
||||
|
||||
/**
|
||||
* @param capacity which restricts the queue size
|
||||
* @param tombValue the value which is guaranteed to not be used in the queue.
|
||||
*/
|
||||
public FixedConcurrentIntQueue(int capacity, int tombValue) {
|
||||
this.capacity = capacity;
|
||||
this.tombValue = tombValue;
|
||||
queue = new AtomicIntegerArray(capacity);
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
queue.set(i, tombValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value to be pushed in the queue
|
||||
* @return value which was evicted off the queue because of the overflow or {@link #tombValue} if no overflow happened
|
||||
*/
|
||||
public int push(int value) {
|
||||
if (value == tombValue) {
|
||||
throw new IllegalArgumentException("Must not use tomb value: "+value);
|
||||
}
|
||||
int index = getAndIncrement();
|
||||
return queue.getAndSet(index, value);
|
||||
}
|
||||
|
||||
private int getAndIncrement() {
|
||||
int index;
|
||||
int next;
|
||||
do {
|
||||
index = tail.get();
|
||||
next = index + 1;
|
||||
if (next >= capacity) {
|
||||
next -= capacity;
|
||||
}
|
||||
} while (!tail.compareAndSet(index, next));
|
||||
return index;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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.util.containers;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An {@code int->V} map with fixed {@code capacity}.
|
||||
* When new key->value placed into the map and its size exceeded the capacity,
|
||||
* the LRU oldKey->oldValue pair (i.e. the pair which was added into the map a {@code capacity} puts ago) is removed.
|
||||
* Thread safe.
|
||||
*/
|
||||
public class LRUConcurrentIntObjectMap<V> {
|
||||
private static final int TOMB_VALUE = -1;
|
||||
/**
|
||||
* the keys are stored in the usual ConcurrentIntObjectMap,
|
||||
* while their LRU order is maintained in {@link #queue}. As soon as the key is evicted from the queue we remove the key->value pair from the map.
|
||||
*/
|
||||
private final ConcurrentIntObjectMap<V> myMap;
|
||||
private final FixedConcurrentIntQueue queue;
|
||||
|
||||
public LRUConcurrentIntObjectMap(int capacity) {
|
||||
queue = new FixedConcurrentIntQueue(capacity - capacity / 4 - 2 /* that's more or less the ConcurrentHashMap size which triggers resize*/, TOMB_VALUE);
|
||||
myMap = ContainerUtil.createConcurrentIntObjectMap(capacity, 1, Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
public V get(int key) {
|
||||
return myMap.get(key);
|
||||
}
|
||||
|
||||
public interface IntFunction<R> {
|
||||
@NotNull
|
||||
R apply(int value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public V computeIfAbsent(int key, @NotNull IntFunction<V> function) {
|
||||
V v = myMap.get(key);
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
|
||||
V newV = function.apply(key);
|
||||
V prev = myMap.putIfAbsent(key, newV);
|
||||
if (prev == null) {
|
||||
addToQueue(key);
|
||||
return newV;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
public V put(int key, V value) {
|
||||
V prev = myMap.put(key, value);
|
||||
if (prev == null) {
|
||||
addToQueue(key);
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
private void addToQueue(int key) {
|
||||
int evicted = queue.push(key);
|
||||
if (evicted != TOMB_VALUE) {
|
||||
myMap.remove(evicted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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.util.containers;
|
||||
|
||||
import gnu.trove.TIntArrayList;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class FixedConcurrentIntQueueTest extends TestCase {
|
||||
public void testSimple() {
|
||||
int tombValue = -1;
|
||||
final FixedConcurrentIntQueue queue = new FixedConcurrentIntQueue(4, tombValue);
|
||||
assertEquals(tombValue, queue.push(2));
|
||||
assertEquals(tombValue, queue.push(3));
|
||||
assertEquals(tombValue, queue.push(4));
|
||||
assertEquals(tombValue, queue.push(5));
|
||||
assertEquals(2, queue.push(6));
|
||||
assertEquals(3, queue.push(7));
|
||||
assertEquals(4, queue.push(8));
|
||||
assertEquals(5, queue.push(9));
|
||||
assertEquals(6, queue.push(0));
|
||||
}
|
||||
|
||||
public void testIntegerOverflow() {
|
||||
int tombValue = -1;
|
||||
final FixedConcurrentIntQueue queue = new FixedConcurrentIntQueue(4, tombValue);
|
||||
for (int i=10; i!=9; i++) {
|
||||
queue.push(2); // must not AIOOBE
|
||||
}
|
||||
}
|
||||
|
||||
public void testStress() throws InterruptedException {
|
||||
for (int k=0; k<100; k++) {
|
||||
final int N = 1 << 20;
|
||||
final FixedConcurrentIntQueue queue = new FixedConcurrentIntQueue(N, -1);
|
||||
final Thread[] threads = new Thread[8];
|
||||
final Set<Integer> result = new ConcurrentHashSet<Integer>();
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
final int finalI = i;
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TIntArrayList evicted = new TIntArrayList();
|
||||
for (int i = N / threads.length * finalI; i < N / threads.length * finalI + N / threads.length; i++) {
|
||||
int f = queue.push(i);
|
||||
evicted.add(f);
|
||||
}
|
||||
for (int f : evicted.toNativeArray()) {
|
||||
result.add(f);
|
||||
}
|
||||
}
|
||||
},i + "");
|
||||
threads[i] = thread;
|
||||
thread.start();
|
||||
}
|
||||
|
||||
for (Thread thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
int size = result.size();
|
||||
assertEquals(1, size);
|
||||
result.clear();
|
||||
for (int i=0; i<N;i++) {
|
||||
result.add(queue.queue.get(i));
|
||||
}
|
||||
assertEquals(N, result.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user