Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

peer: Rename MRU to LRU. #976

Merged
merged 3 commits into from
Feb 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 32 additions & 32 deletions peer/mruinvmap.go → peer/lruinvcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,27 @@ import (
"github.com/decred/dcrd/wire"
)

// mruInventoryMap provides a concurrency safe map that is limited to a maximum
// lruInventoryCache provides a concurrency safe cache that is limited to a maximum
// number of items with eviction for the oldest entry when the limit is
// exceeded.
type mruInventoryMap struct {
invMtx sync.Mutex
invMap map[wire.InvVect]*list.Element // nearly O(1) lookups
invList *list.List // O(1) insert, update, delete
limit uint
type lruInventoryCache struct {
invMtx sync.Mutex
invCache map[wire.InvVect]*list.Element // nearly O(1) lookups
invList *list.List // O(1) insert, update, delete
limit uint
}

// String returns the map as a human-readable string.
// String returns the cache as a human-readable string.
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) String() string {
func (m *lruInventoryCache) String() string {
m.invMtx.Lock()
defer m.invMtx.Unlock()

lastEntryNum := len(m.invMap) - 1
lastEntryNum := len(m.invCache) - 1
curEntry := 0
buf := bytes.NewBufferString("[")
for iv := range m.invMap {
for iv := range m.invCache {
buf.WriteString(fmt.Sprintf("%v", iv))
if curEntry < lastEntryNum {
buf.WriteString(", ")
Expand All @@ -46,83 +46,83 @@ func (m *mruInventoryMap) String() string {
return fmt.Sprintf("<%d>%s", m.limit, buf.String())
}

// Exists returns whether or not the passed inventory item is in the map.
// Exists returns whether or not the passed inventory item is in the cache.
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) Exists(iv *wire.InvVect) bool {
func (m *lruInventoryCache) Exists(iv *wire.InvVect) bool {
m.invMtx.Lock()
_, exists := m.invMap[*iv]
_, exists := m.invCache[*iv]
m.invMtx.Unlock()

return exists
}

// Add adds the passed inventory to the map and handles eviction of the oldest
// Add adds the passed inventory to the cache and handles eviction of the oldest
// item if adding the new item would exceed the max limit. Adding an existing
// item makes it the most recently used item.
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) Add(iv *wire.InvVect) {
func (m *lruInventoryCache) Add(iv *wire.InvVect) {
m.invMtx.Lock()
defer m.invMtx.Unlock()

// When the limit is zero, nothing can be added to the map, so just
// When the limit is zero, nothing can be added to the cache, so just
// return.
if m.limit == 0 {
return
}

// When the entry already exists move it to the front of the list
// thereby marking it most recently used.
if node, exists := m.invMap[*iv]; exists {
if node, exists := m.invCache[*iv]; exists {
m.invList.MoveToFront(node)
return
}

// Evict the least recently used entry (back of the list) if the the new
// entry would exceed the size limit for the map. Also reuse the list
// entry would exceed the size limit for the cache. Also reuse the list
// node so a new one doesn't have to be allocated.
if uint(len(m.invMap))+1 > m.limit {
if uint(len(m.invCache))+1 > m.limit {
node := m.invList.Back()
lru := node.Value.(*wire.InvVect)

// Evict least recently used item.
delete(m.invMap, *lru)
delete(m.invCache, *lru)

// Reuse the list node of the item that was just evicted for the
// new item.
node.Value = iv
m.invList.MoveToFront(node)
m.invMap[*iv] = node
m.invCache[*iv] = node
return
}

// The limit hasn't been reached yet, so just add the new item.
node := m.invList.PushFront(iv)
m.invMap[*iv] = node
m.invCache[*iv] = node
}

// Delete deletes the passed inventory item from the map (if it exists).
// Delete deletes the passed inventory item from the cache (if it exists).
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) Delete(iv *wire.InvVect) {
func (m *lruInventoryCache) Delete(iv *wire.InvVect) {
m.invMtx.Lock()
if node, exists := m.invMap[*iv]; exists {
if node, exists := m.invCache[*iv]; exists {
m.invList.Remove(node)
delete(m.invMap, *iv)
delete(m.invCache, *iv)
}
m.invMtx.Unlock()
}

// newMruInventoryMap returns a new inventory map that is limited to the number
// newLruInventoryCache returns a new inventory cache that is limited to the number
// of entries specified by limit. When the number of entries exceeds the limit,
// the oldest (least recently used) entry will be removed to make room for the
// new entry.
func newMruInventoryMap(limit uint) *mruInventoryMap {
m := mruInventoryMap{
invMap: make(map[wire.InvVect]*list.Element),
invList: list.New(),
limit: limit,
func newLruInventoryCache(limit uint) *lruInventoryCache {
m := lruInventoryCache{
invCache: make(map[wire.InvVect]*list.Element),
invList: list.New(),
limit: limit,
}
return &m
}
66 changes: 33 additions & 33 deletions peer/mruinvmap_test.go → peer/lruinvcache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import (
"github.com/decred/dcrd/wire"
)

// TestMruInventoryMap ensures the MruInventoryMap behaves as expected including
// TestLruInventoryCache ensures the LruInventoryCache behaves as expected including
// limiting, eviction of least-recently used entries, specific entry removal,
// and existence tests.
func TestMruInventoryMap(t *testing.T) {
// Create a bunch of fake inventory vectors to use in testing the mru
func TestLruInventoryCache(t *testing.T) {
// Create a bunch of fake inventory vectors to use in testing the lru
// inventory code.
numInvVects := 10
invVects := make([]*wire.InvVect, 0, numInvVects)
Expand All @@ -42,19 +42,19 @@ func TestMruInventoryMap(t *testing.T) {

testLoop:
for i, test := range tests {
// Create a new mru inventory map limited by the specified test
// Create a new lru inventory cache limited by the specified test
// limit and add all of the test inventory vectors. This will
// cause evicition since there are more test inventory vectors
// than the limits.
mruInvMap := newMruInventoryMap(uint(test.limit))
lruInvCache := newLruInventoryCache(uint(test.limit))
for j := 0; j < numInvVects; j++ {
mruInvMap.Add(invVects[j])
lruInvCache.Add(invVects[j])
}

// Ensure the limited number of most recent entries in the
// inventory vector list exist.
for j := numInvVects - test.limit; j < numInvVects; j++ {
if !mruInvMap.Exists(invVects[j]) {
if !lruInvCache.Exists(invVects[j]) {
t.Errorf("Exists #%d (%s) entry %s does not "+
"exist", i, test.name, *invVects[j])
continue testLoop
Expand All @@ -64,7 +64,7 @@ testLoop:
// Ensure the entries before the limited number of most recent
// entries in the inventory vector list do not exist.
for j := 0; j < numInvVects-test.limit; j++ {
if mruInvMap.Exists(invVects[j]) {
if lruInvCache.Exists(invVects[j]) {
t.Errorf("Exists #%d (%s) entry %s exists", i,
test.name, *invVects[j])
continue testLoop
Expand All @@ -80,36 +80,36 @@ testLoop:
// This check needs at least 2 entries.
if test.limit > 1 {
origLruIndex := numInvVects - test.limit
mruInvMap.Add(invVects[origLruIndex])
lruInvCache.Add(invVects[origLruIndex])

iv := wire.NewInvVect(wire.InvTypeBlock,
&chainhash.Hash{0x00, 0x01})
mruInvMap.Add(iv)
lruInvCache.Add(iv)

// Ensure the original lru entry still exists since it
// was updated and should've have become the mru entry.
if !mruInvMap.Exists(invVects[origLruIndex]) {
t.Errorf("MRU #%d (%s) entry %s does not exist",
// was updated and should've have become the lru entry.
if !lruInvCache.Exists(invVects[origLruIndex]) {
t.Errorf("LRU #%d (%s) entry %s does not exist",
i, test.name, *invVects[origLruIndex])
continue testLoop
}

// Ensure the entry that should've become the new lru
// entry was evicted.
newLruIndex := origLruIndex + 1
if mruInvMap.Exists(invVects[newLruIndex]) {
t.Errorf("MRU #%d (%s) entry %s exists", i,
if lruInvCache.Exists(invVects[newLruIndex]) {
t.Errorf("LRU #%d (%s) entry %s exists", i,
test.name, *invVects[newLruIndex])
continue testLoop
}
}

// Delete all of the entries in the inventory vector list,
// including those that don't exist in the map, and ensure they
// including those that don't exist in the cache, and ensure they
// no longer exist.
for j := 0; j < numInvVects; j++ {
mruInvMap.Delete(invVects[j])
if mruInvMap.Exists(invVects[j]) {
lruInvCache.Delete(invVects[j])
if lruInvCache.Exists(invVects[j]) {
t.Errorf("Delete #%d (%s) entry %s exists", i,
test.name, *invVects[j])
continue testLoop
Expand All @@ -118,38 +118,38 @@ testLoop:
}
}

// TestMruInventoryMapStringer tests the stringized output for the
// MruInventoryMap type.
func TestMruInventoryMapStringer(t *testing.T) {
// Create a couple of fake inventory vectors to use in testing the mru
// TestLruInventoryCacheStringer tests the stringized output for the
// LruInventoryCache type.
func TestLruInventoryCacheStringer(t *testing.T) {
// Create a couple of fake inventory vectors to use in testing the lru
// inventory stringer code.
hash1 := &chainhash.Hash{0x01}
hash2 := &chainhash.Hash{0x02}
iv1 := wire.NewInvVect(wire.InvTypeBlock, hash1)
iv2 := wire.NewInvVect(wire.InvTypeBlock, hash2)

// Create new mru inventory map and add the inventory vectors.
mruInvMap := newMruInventoryMap(uint(2))
mruInvMap.Add(iv1)
mruInvMap.Add(iv2)
// Create new lru inventory cache and add the inventory vectors.
lruInvCache := newLruInventoryCache(uint(2))
lruInvCache.Add(iv1)
lruInvCache.Add(iv2)

// Ensure the stringer gives the expected result. Since map iteration
// Ensure the stringer gives the expected result. Since cache iteration
// is not ordered, either entry could be first, so account for both
// cases.
wantStr1 := fmt.Sprintf("<%d>[%s, %s]", 2, *iv1, *iv2)
wantStr2 := fmt.Sprintf("<%d>[%s, %s]", 2, *iv2, *iv1)
gotStr := mruInvMap.String()
gotStr := lruInvCache.String()
if gotStr != wantStr1 && gotStr != wantStr2 {
t.Fatalf("unexpected string representation - got %q, want %q "+
"or %q", gotStr, wantStr1, wantStr2)
}
}

// BenchmarkMruInventoryList performs basic benchmarks on the most recently
// BenchmarkLruInventoryList performs basic benchmarks on the most recently
// used inventory handling.
func BenchmarkMruInventoryList(b *testing.B) {
func BenchmarkLruInventoryList(b *testing.B) {
// Create a bunch of fake inventory vectors to use in benchmarking
// the mru inventory code.
// the lru inventory code.
b.StopTimer()
numInvVects := 100000
invVects := make([]*wire.InvVect, 0, numInvVects)
Expand All @@ -164,8 +164,8 @@ func BenchmarkMruInventoryList(b *testing.B) {

// Benchmark the add plus evicition code.
limit := 20000
mruInvMap := newMruInventoryMap(uint(limit))
lruInvCache := newLruInventoryCache(uint(limit))
for i := 0; i < b.N; i++ {
mruInvMap.Add(invVects[i%numInvVects])
lruInvCache.Add(invVects[i%numInvVects])
}
}
Loading