-
Notifications
You must be signed in to change notification settings - Fork 130
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
[runtime] Add an in-memory cache for Benchmark protos. #263
[runtime] Add an in-memory cache for Benchmark protos. #263
Conversation
809ab76
to
aa5fe5a
Compare
Codecov Report
@@ Coverage Diff @@
## development #263 +/- ##
===============================================
+ Coverage 83.32% 83.46% +0.13%
===============================================
Files 76 78 +2
Lines 4353 4426 +73
===============================================
+ Hits 3627 3694 +67
- Misses 726 732 +6
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
maxSizeInBytes_(maxSizeInBytes), | ||
sizeInBytes_(0){}; | ||
|
||
const Benchmark* BenchmarkCache::get(const std::string& uri) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider (but not for long, you know what I'm like):
class BenchmarkCache {
public:
BenchmarkCache(std::function<Benchmark(string url)> benchmarkCreatorThingie, ...)...
...
Benchmark& operator[] (string url) {
if (url in cache) return cache[url];
auto b = benchmarkCreatorThingie(url); // May throw std::out_of_range like std::vector.at
if (doesn't have capacity) {
evictUntilCapacity(b.size());
}
cache[url] = b;
return b;
}
}```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if that makes sense for this use case because there is no std::function<Benchmark(string url)> benchmarkCreatorThingie
callback that would make sense. If the benchmark can't be found, we return an error to the frontend. Pseudo-code:
class CompilerGymRuntime:
def session_rpc_endpoint(self, benchmark_uri):
if benchmark_uri not in self.benchmark_cache:
return ErrorCode.NOT_FOUND
benchmark = self.benchmark_cache[benchmark_uri]
...
targetSize = targetSize.has_value() ? targetSize : maxSizeInBytes() / 2; | ||
|
||
while (size() && sizeInBytes() > targetSize) { | ||
// Select a benchmark randomly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignorable: I always thought the advantages of random over lru were that random doesn't need to keep the recently used list. If the data items are small that makes sense. Otherwise, isn't lru pretty much always better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured that there are two use common cases for this cache: (1) in a tight loop over a couple hundred benchmarks that will all fit in cache (2) over a massive set of training programs where the chance of a cache hit is negligible.
Given that, it didn't seem to me like LRU would be much of an advantage. I also found this interesting: "A random eviction policy degrades gracefully as the loop gets too big." from https://danluu.com/2choices-eviction/
Disclaimer: I know nothing about cache eviction policies and have utterly no idea what I'm talking about :)
Incorporate reviewer feedback on facebookresearch#263.
This will be used by the CompilationSession runtime to keep track of the Benchmark protobufs that have been sent by the user to the service, so that CompilationSession::init() can be passed a benchmark proto. This is a generalization of the BenchmarkFactory class that is used by the LLVM service to keep a bunch of llvm::Modules loaded in memory. The same class is implemented twice in C++ and Python using the same semantics and with the same tests. The cache has a target maximum size based on the number of bytes of its elements. When this size is reached, benchamrks are evicted using a random policy. The idea behind random cache eviction is that this cache will be large enough by default to store a good number of benchmarks, so exceeding the max cache size implies a training loop in which random programs are selected from a very large pool, rather than smaller pool where an LRU policy would be better. Issue facebookresearch#254.
Incorporate reviewer feedback on facebookresearch#263.
be24e9b
to
d8715f6
Compare
Incorporate reviewer feedback on facebookresearch#263.
This will be used by the CompilationSession runtime to keep track of
the Benchmark protobufs that have been sent by the user to the
service, so that
CompilationSession::init()
can be passed a benchmarkproto.
This is a generalization of the BenchmarkFactory class that is used by
the LLVM service to keep a bunch of llvm::Modules loaded in
memory. The same class is implemented twice in C++ and Python using
the same semantics and with the same tests.
The cache has a target maximum size based on the number of bytes of
its elements. When this size is reached, benchamrks are evicted using
a random policy. The idea behind random cache eviction is that this
cache will be large enough by default to store a good number of
benchmarks, so exceeding the max cache size implies a training loop in
which random programs are selected from a very large pool, rather than
smaller pool where an LRU policy would be better.
Issue #254.