-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from bodgit/speedup
Implement pooling of decompressed folder readers
- Loading branch information
Showing
10 changed files
with
1,201 additions
and
859 deletions.
There are no files selected for viewing
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
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
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,137 @@ | ||
package pool | ||
|
||
import ( | ||
"container/list" | ||
"runtime" | ||
"sort" | ||
"sync" | ||
|
||
"github.com/bodgit/sevenzip/internal/util" | ||
) | ||
|
||
// Pooler is the interface implemented by a pool. | ||
type Pooler interface { | ||
Get(int64) (util.SizeReadSeekCloser, bool) | ||
Put(int64, util.SizeReadSeekCloser) (bool, error) | ||
} | ||
|
||
// Constructor is the function prototype used to instantiate a pool. | ||
type Constructor func() (Pooler, error) | ||
|
||
type noopPool struct{} | ||
|
||
// NewNoopPool returns a Pooler that doesn't actually pool anything. | ||
func NewNoopPool() (Pooler, error) { | ||
return new(noopPool), nil | ||
} | ||
|
||
func (noopPool) Get(_ int64) (util.SizeReadSeekCloser, bool) { | ||
return nil, false | ||
} | ||
|
||
func (noopPool) Put(_ int64, rc util.SizeReadSeekCloser) (bool, error) { | ||
return false, rc.Close() | ||
} | ||
|
||
type pool struct { | ||
mutex sync.Mutex | ||
size int | ||
evictList *list.List | ||
items map[int64]*list.Element | ||
} | ||
|
||
type entry struct { | ||
key int64 | ||
value util.SizeReadSeekCloser | ||
} | ||
|
||
// NewPool returns a Pooler that uses a LRU strategy to maintain a fixed pool | ||
// of util.SizeReadSeekCloser's keyed by their stream offset. | ||
func NewPool() (Pooler, error) { | ||
return &pool{ | ||
size: runtime.NumCPU(), | ||
evictList: list.New(), | ||
items: make(map[int64]*list.Element), | ||
}, nil | ||
} | ||
|
||
func (p *pool) Get(offset int64) (util.SizeReadSeekCloser, bool) { | ||
p.mutex.Lock() | ||
defer p.mutex.Unlock() | ||
|
||
if ent, ok := p.items[offset]; ok { | ||
_ = p.removeElement(ent, false) | ||
|
||
return ent.Value.(*entry).value, true //nolint:forcetypeassert | ||
} | ||
|
||
// Sort keys in descending order | ||
keys := p.keys() | ||
sort.Slice(keys, func(i, j int) bool { return keys[i] > keys[j] }) | ||
|
||
for _, k := range keys { | ||
// First key less than offset is the closest | ||
if k < offset { | ||
ent := p.items[k] | ||
_ = p.removeElement(ent, false) | ||
|
||
return ent.Value.(*entry).value, true //nolint:forcetypeassert | ||
} | ||
} | ||
|
||
return nil, false | ||
} | ||
|
||
func (p *pool) Put(offset int64, rc util.SizeReadSeekCloser) (bool, error) { | ||
p.mutex.Lock() | ||
defer p.mutex.Unlock() | ||
|
||
if _, ok := p.items[offset]; ok { | ||
return false, nil | ||
} | ||
|
||
ent := &entry{offset, rc} | ||
entry := p.evictList.PushFront(ent) | ||
p.items[offset] = entry | ||
|
||
var err error | ||
|
||
evict := p.evictList.Len() > p.size | ||
if evict { | ||
err = p.removeOldest() | ||
} | ||
|
||
return evict, err | ||
} | ||
|
||
func (p *pool) keys() []int64 { | ||
keys := make([]int64, len(p.items)) | ||
i := 0 | ||
|
||
for ent := p.evictList.Back(); ent != nil; ent = ent.Prev() { | ||
keys[i] = ent.Value.(*entry).key //nolint:forcetypeassert | ||
i++ | ||
} | ||
|
||
return keys | ||
} | ||
|
||
func (p *pool) removeOldest() error { | ||
if ent := p.evictList.Back(); ent != nil { | ||
return p.removeElement(ent, true) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (p *pool) removeElement(e *list.Element, cb bool) error { | ||
p.evictList.Remove(e) | ||
kv := e.Value.(*entry) //nolint:forcetypeassert | ||
delete(p.items, kv.key) | ||
|
||
if cb { | ||
return kv.value.Close() | ||
} | ||
|
||
return nil | ||
} |
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
Oops, something went wrong.