-
Notifications
You must be signed in to change notification settings - Fork 73
Improve memory efficiency of seen cache #1073
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
Changes from all commits
9cb8208
4254ced
3c74db5
fd9f9ab
513ee0b
d425eb2
6c42af5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,12 +9,13 @@ | |
|
|
||
| {.push raises: [].} | ||
|
|
||
| import std/[tables] | ||
|
|
||
| import std/[hashes, sets] | ||
| import chronos/timer, stew/results | ||
|
|
||
| import ../../utility | ||
|
|
||
| export results | ||
|
|
||
| const Timeout* = 10.seconds # default timeout in ms | ||
|
|
||
| type | ||
|
|
@@ -26,20 +27,38 @@ type | |
|
|
||
| TimedCache*[K] = object of RootObj | ||
| head, tail: TimedEntry[K] # nim linked list doesn't allow inserting at pos | ||
| entries: Table[K, TimedEntry[K]] | ||
| entries: HashSet[TimedEntry[K]] | ||
| timeout: Duration | ||
|
|
||
| func `==`*[E](a, b: TimedEntry[E]): bool = | ||
| if isNil(a) == isNil(b): | ||
| isNil(a) or a.key == b.key | ||
| else: | ||
| false | ||
|
|
||
| func hash*(a: TimedEntry): Hash = | ||
| if isNil(a): | ||
| default(Hash) | ||
| else: | ||
| hash(a[].key) | ||
|
|
||
| func expire*(t: var TimedCache, now: Moment = Moment.now()) = | ||
| while t.head != nil and t.head.expiresAt < now: | ||
| t.entries.del(t.head.key) | ||
| t.entries.excl(t.head) | ||
| t.head.prev = nil | ||
| t.head = t.head.next | ||
| if t.head == nil: t.tail = nil | ||
|
|
||
| func del*[K](t: var TimedCache[K], key: K): Opt[TimedEntry[K]] = | ||
| # Removes existing key from cache, returning the previous value if present | ||
| var item: TimedEntry[K] | ||
| if t.entries.pop(key, item): | ||
| let tmp = TimedEntry[K](key: key) | ||
| if tmp in t.entries: | ||
| let item = try: | ||
| t.entries[tmp] # use the shared instance in the set | ||
| except KeyError: | ||
| raiseAssert "just checked" | ||
| t.entries.excl(item) | ||
|
|
||
| if t.head == item: t.head = item.next | ||
| if t.tail == item: t.tail = item.prev | ||
|
|
||
|
|
@@ -55,14 +74,14 @@ func put*[K](t: var TimedCache[K], k: K, now = Moment.now()): bool = | |
| # refreshed. | ||
| t.expire(now) | ||
|
|
||
| var previous = t.del(k) # Refresh existing item | ||
|
|
||
| var addedAt = now | ||
| previous.withValue(previous): | ||
| addedAt = previous.addedAt | ||
| let | ||
| previous = t.del(k) # Refresh existing item | ||
| addedAt = if previous.isSome(): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We had a long PR in the past to remove this pattern from the codebase and decrease the risk of raising defects. You can use https://github.com/vacp2p/nim-libp2p/blob/unstable/libp2p/utility.nim#L125
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, but
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doesn't work in generic code, due to similar problems as arnetheduck/nim-results#34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this seems to work fine: |
||
| previous[].addedAt | ||
| else: | ||
| now | ||
|
|
||
| let node = TimedEntry[K](key: k, addedAt: addedAt, expiresAt: now + t.timeout) | ||
|
|
||
| if t.head == nil: | ||
| t.tail = node | ||
| t.head = t.tail | ||
|
|
@@ -83,16 +102,24 @@ func put*[K](t: var TimedCache[K], k: K, now = Moment.now()): bool = | |
| if cur == t.tail: | ||
| t.tail = node | ||
|
|
||
| t.entries[k] = node | ||
| t.entries.incl(node) | ||
|
|
||
| previous.isSome() | ||
|
|
||
| func contains*[K](t: TimedCache[K], k: K): bool = | ||
| k in t.entries | ||
|
|
||
| func addedAt*[K](t: TimedCache[K], k: K): Moment = | ||
| t.entries.getOrDefault(k).addedAt | ||
|
|
||
| let tmp = TimedEntry[K](key: k) | ||
| tmp in t.entries | ||
|
|
||
| func addedAt*[K](t: var TimedCache[K], k: K): Moment = | ||
|
diegomrsantos marked this conversation as resolved.
|
||
| let tmp = TimedEntry[K](key: k) | ||
| try: | ||
| if tmp in t.entries: # raising is slow | ||
| # Use shared instance from entries | ||
| return t.entries[tmp][].addedAt | ||
| except KeyError: | ||
| raiseAssert "just checked" | ||
|
|
||
| default(Moment) | ||
|
|
||
| func init*[K](T: type TimedCache[K], timeout: Duration = Timeout): T = | ||
| T( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.