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
DM-13854: add general-purpose Cache class #50
Conversation
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.
Looks good. I've got a few miscellaneous comments.
In the class documentation I've asked you to write, please also note that:
Value
andKey
must be Copyable.- This header should generally only be included in source files, not other headers, because we really don't want all of that
multi_index_container
stuff being included in source files where it isn't needed. Since this is a class that will be frequently used as a data member, that in turn means that it should usually be held viaunique_ptr
so it can be forward-declared in the header file.
And to make that last bit of advice easier for users to follow, it might be good to make a CacheFwd.h
header file that does that forward-declaration for them. Note that the forward declaration needs to include the template parameter defaults, and then you'll need to remove them from Cache.h
and have it include CacheFwd.h
.
include/lsst/utils/Cache.h
Outdated
namespace utils { | ||
|
||
/// Cache of most recently used values | ||
template <typename Key, typename Value, typename KeyHash=boost::hash<Key>, |
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.
C++11 has a std::hash
that should be preferred.
include/lsst/utils/Cache.h
Outdated
#include <boost/multi_index/composite_key.hpp> | ||
#include <boost/multi_index/member.hpp> | ||
#include <boost/format.hpp> | ||
#include <boost/functional/hash.hpp> // boost::hash |
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.
LSST convention is to use ""
instead of <>
for all but system headers.
include/lsst/utils/Cache.h
Outdated
namespace lsst { | ||
namespace utils { | ||
|
||
/// Cache of most recently used values |
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.
Would like to see a bit more in the way of class docs than this.
I believe we've also standardized on using /** ... */
for all but single-line docstrings (https://developer.lsst.io/cpp/api-docs.html#documentation-must-be-delimited-in-javadoc-style).
/// Dtor | ||
#ifdef LSST_CACHE_DEBUG | ||
/// | ||
/// Saves the cache requests to file. |
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.
This makes me a bit uncomfortable; having destructors that could possibly throw (and anything that writes could definitely throw) causes headaches. But I suppose if it's only debug mode it's probably okay.
include/lsst/utils/Cache.h
Outdated
/// possible the user has some function that produces a value | ||
/// when given a key, so chose to adopt that signature; any other | ||
/// signature would likely require use of a lambda always. | ||
Value operator()(Key const& key, std::function<Value(Key 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.
While it provides a nice way to document and enforce the function signature you're expecting, std::function
comes with virtual function call overheads, so I usually wouldn't use it unless I need its polymorphism. Might be best to just take a template parameter here, since the implementation has to go in the header anyway. But that said, I suppose we don't cache something unless calculating it is slow enough that virtual function overheads shouldn't matter.
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 went with std::function
because pybind wraps it without any effort on my part (yay, calling python from C++!). I changed the C++ to template
the function type for pure speed, and converted the pybind wrapper to use std::function
so that's still handled simply.
include/lsst/utils/Cache.h
Outdated
/// Lookup a value | ||
/// | ||
/// The key must be in the cache already, or an | ||
/// lsst::pex::exceptions::NotFoundError will be raised. |
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.
See DevGuide for how to document possible exceptions in Doxygen (and on that note, it would be good to document the exception safety of all of these methods).
include/lsst/utils/Cache.h
Outdated
} | ||
|
||
std::size_t _maxElements; // Maximum number of elements; 0 means infinite | ||
mutable Container _container; // Container of key,value pairs |
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 think it might be cleaner to make Container
non-mutable
, even if that requires more classes that use it to make their Cache
data members mutable. I imagine most of those will be calling operator()
rather than operator[]
anyway, so the constness of operator[]
doesn't gain them much.
include/lsst/utils/Cache.h
Outdated
Value operator[](Key const& key) const; | ||
|
||
/// Add a value to the cache | ||
void add(Key const& key, Value const& value); |
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.
Should document behavior if the key is already in the cache.
return numberToWords(hundreds) + " hundred" + ((" " + numberToWords(rest)) if rest > 0 else "") | ||
|
||
|
||
class CacheTestCase(lsst.utils.tests.TestCase): |
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.
newline here.
include/lsst/utils/Cache.h
Outdated
void flush(); | ||
|
||
#ifdef LSST_CACHE_DEBUG | ||
void enableDebugging() { debuggingEnabled = true; } |
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.
Doesn't this need an underscore?
include/lsst/utils/Cache.h
Outdated
_container.template project<Sequence>(it)); | ||
} | ||
#ifdef LSST_CACHE_DEBUG | ||
if (debuggingEnabled) { |
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.
And here...
include/lsst/utils/Cache.h
Outdated
std::size_t _maxElements; // Maximum number of elements; 0 means infinite | ||
mutable Container _container; // Container of key,value pairs | ||
#ifdef LSST_CACHE_DEBUG | ||
bool debuggingEnabled; |
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.
And here.
ae36ebb
to
4deba98
Compare
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.
Looks good, thanks for the improvements!
a15fedf
to
2f06bc9
Compare
Cache of most recently used objects, hashed by some key, with user-defined capacity limit.
Allows us to characterise the cache performance.
C++11 added std::hash, but neglected to include a function for combining multiple hashes.
2f06bc9
to
e68ac28
Compare
No description provided.