From b99f18399bd7178fc0bc640c97f08f0fdbd82d49 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 16 Sep 2015 14:54:11 +0300 Subject: [PATCH] Reverted --- .../vfs/newvfs/impl/FileNameCache.java | 95 ++++++++++++++++--- .../containers/FixedConcurrentIntQueue.java | 70 -------------- .../containers/LRUConcurrentIntObjectMap.java | 79 --------------- .../FixedConcurrentIntQueueTest.java | 84 ---------------- 4 files changed, 82 insertions(+), 246 deletions(-) delete mode 100644 platform/util/src/com/intellij/util/containers/FixedConcurrentIntQueue.java delete mode 100644 platform/util/src/com/intellij/util/containers/LRUConcurrentIntObjectMap.java delete mode 100644 platform/util/testSrc/com/intellij/util/containers/FixedConcurrentIntQueueTest.java diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java index a84f93352017..fc09206f91dc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java @@ -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 ourNameCache = new LRUConcurrentIntObjectMap(65000); + private static final PersistentStringEnumerator ourNames = FSRecords.getNames(); + @SuppressWarnings("unchecked") private static final IntSLRUCache>[] 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>(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 getNameById = new LRUConcurrentIntObjectMap.IntFunction() { - @NotNull - @Override - public CharSequence apply(int id) { - String name = FSRecords.getNameByNameId(id); - return ByteArrayCharSequence.convertToBytesIfAsciiString(name); + @NotNull + private static IntObjectLinkedMap.MapEntry 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 entry = new IntObjectLinkedMap.MapEntry(id, rawName); + IntSLRUCache> 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[] 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 entry = ourArrayCache[l1]; + if (entry != null && entry.key == nameId) { + return entry.value; + } + + if (ourTrackStats) { + ourMisses.incrementAndGet(); + } + + final int stripe = calcStripeIdFromNameId(nameId); + IntSLRUCache> 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; } } diff --git a/platform/util/src/com/intellij/util/containers/FixedConcurrentIntQueue.java b/platform/util/src/com/intellij/util/containers/FixedConcurrentIntQueue.java deleted file mode 100644 index 5934861086a0..000000000000 --- a/platform/util/src/com/intellij/util/containers/FixedConcurrentIntQueue.java +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/containers/LRUConcurrentIntObjectMap.java b/platform/util/src/com/intellij/util/containers/LRUConcurrentIntObjectMap.java deleted file mode 100644 index 8d0f5a617cb6..000000000000 --- a/platform/util/src/com/intellij/util/containers/LRUConcurrentIntObjectMap.java +++ /dev/null @@ -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 { - 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 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 { - @NotNull - R apply(int value); - } - - @NotNull - public V computeIfAbsent(int key, @NotNull IntFunction 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); - } - } -} diff --git a/platform/util/testSrc/com/intellij/util/containers/FixedConcurrentIntQueueTest.java b/platform/util/testSrc/com/intellij/util/containers/FixedConcurrentIntQueueTest.java deleted file mode 100644 index fe8c660d7fda..000000000000 --- a/platform/util/testSrc/com/intellij/util/containers/FixedConcurrentIntQueueTest.java +++ /dev/null @@ -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 result = new ConcurrentHashSet(); - 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