forked from Alluxio/alluxio
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cherry-pick and resolve location conflict: Add the two-choice random …
…cache eviction policy The PR adds the two-choice random cache eviction policy. The algorithm selects two random page IDs and evicts the one least recently used. From some evaluation (https://danluu.com/2choices-eviction/), the two-choice random policy has competitive performance compared with LRU. Users can configure the new cache eviction policy. pr-link: Alluxio#16828 change-id: cid-f9a140208b3a6938ddc70766abfb3d9f90c722f0
- Loading branch information
1 parent
ae84f7c
commit e15d0b0
Showing
2 changed files
with
175 additions
and
0 deletions.
There are no files selected for viewing
94 changes: 94 additions & 0 deletions
94
...ore/client/fs/src/main/java/alluxio/client/file/cache/evictor/TwoChoiceRandomEvictor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* | ||
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 | ||
* (the "License"). You may not use this work except in compliance with the License, which is | ||
* available at www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied, as more fully set forth in the License. | ||
* | ||
* See the NOTICE file distributed with this work for information regarding copyright ownership. | ||
*/ | ||
|
||
package alluxio.client.file.cache.evictor; | ||
|
||
import alluxio.client.file.cache.PageId; | ||
|
||
import java.time.Instant; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Random; | ||
import java.util.function.Predicate; | ||
import javax.annotation.Nullable; | ||
import javax.annotation.concurrent.ThreadSafe; | ||
|
||
/** | ||
* Two Choice Random client-side cache eviction policy. | ||
* It selects two random page IDs and evicts the one least-recently used. | ||
*/ | ||
@ThreadSafe | ||
public class TwoChoiceRandomEvictor implements CacheEvictor { | ||
private final Map<PageId, Long> mCache = Collections.synchronizedMap(new HashMap<>()); | ||
|
||
/** | ||
* Constructor. | ||
* @param options | ||
*/ | ||
public TwoChoiceRandomEvictor(CacheEvictorOptions options) { | ||
} | ||
|
||
@Override | ||
public void updateOnGet(PageId pageId) { | ||
mCache.put(pageId, Instant.now().toEpochMilli()); | ||
} | ||
|
||
@Override | ||
public void updateOnPut(PageId pageId) { | ||
mCache.put(pageId, Instant.now().toEpochMilli()); | ||
} | ||
|
||
@Override | ||
public void updateOnDelete(PageId pageId) { | ||
mCache.remove(pageId); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public PageId evict() { | ||
synchronized (mCache) { | ||
if (mCache.isEmpty()) { | ||
return null; | ||
} | ||
|
||
// TODO(chunxu): improve the performance here | ||
List<PageId> keys = new ArrayList<>(mCache.keySet()); | ||
Random rand = new Random(); | ||
PageId key1 = keys.get(rand.nextInt(keys.size())); | ||
PageId key2 = keys.get(rand.nextInt(keys.size())); | ||
if (mCache.get(key1) < mCache.get(key2)) { | ||
return key1; | ||
} | ||
return key2; | ||
} | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public PageId evictMatching(Predicate<PageId> criterion) { | ||
synchronized (mCache) { | ||
for (PageId candidate : mCache.keySet()) { | ||
if (criterion.test(candidate)) { | ||
return candidate; | ||
} | ||
} | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
public void reset() { | ||
mCache.clear(); | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
dora/core/client/fs/src/test/java/alluxio/client/file/cache/TwoChoiceRandomEvictorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 | ||
* (the "License"). You may not use this work except in compliance with the License, which is | ||
* available at www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied, as more fully set forth in the License. | ||
* | ||
* See the NOTICE file distributed with this work for information regarding copyright ownership. | ||
*/ | ||
|
||
package alluxio.client.file.cache; | ||
|
||
import alluxio.client.file.cache.evictor.CacheEvictorOptions; | ||
import alluxio.client.file.cache.evictor.TwoChoiceRandomEvictor; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
/** | ||
* Tests for the {@link TwoChoiceRandomEvictor} class. | ||
*/ | ||
public class TwoChoiceRandomEvictorTest { | ||
private TwoChoiceRandomEvictor mEvictor; | ||
private final PageId mFirst = new PageId("1L", 2L); | ||
private final PageId mSecond = new PageId("3L", 4L); | ||
private final PageId mThird = new PageId("5L", 6L); | ||
|
||
/** | ||
* Sets up the instances. | ||
*/ | ||
@Before | ||
public void before() { | ||
mEvictor = new TwoChoiceRandomEvictor(new CacheEvictorOptions()); | ||
} | ||
|
||
@Test | ||
public void evictGetOrder() { | ||
mEvictor.updateOnGet(mFirst); | ||
Assert.assertEquals(mFirst, mEvictor.evict()); | ||
mEvictor.updateOnGet(mSecond); | ||
Assert.assertEquals(mSecond, mEvictor.evict()); | ||
} | ||
|
||
@Test | ||
public void evictPutOrder() { | ||
mEvictor.updateOnPut(mFirst); | ||
Assert.assertEquals(mFirst, mEvictor.evict()); | ||
mEvictor.updateOnPut(mSecond); | ||
mEvictor.updateOnPut(mFirst); | ||
PageId evictedPage = mEvictor.evict(); | ||
Assert.assertTrue(evictedPage.equals(mFirst) || evictedPage.equals(mSecond)); | ||
} | ||
|
||
@Test | ||
public void evictAfterDelete() { | ||
mEvictor.updateOnPut(mFirst); | ||
mEvictor.updateOnPut(mSecond); | ||
mEvictor.updateOnPut(mThird); | ||
mEvictor.updateOnDelete(mSecond); | ||
mEvictor.updateOnDelete(mThird); | ||
Assert.assertEquals(mFirst, mEvictor.evict()); | ||
} | ||
|
||
@Test | ||
public void evictEmpty() { | ||
Assert.assertNull(mEvictor.evict()); | ||
} | ||
|
||
@Test | ||
public void evictAllGone() { | ||
mEvictor.updateOnPut(mFirst); | ||
mEvictor.updateOnPut(mSecond); | ||
mEvictor.updateOnPut(mThird); | ||
mEvictor.updateOnDelete(mFirst); | ||
mEvictor.updateOnDelete(mSecond); | ||
mEvictor.updateOnDelete(mThird); | ||
Assert.assertNull(mEvictor.evict()); | ||
} | ||
} |