Last active
October 13, 2016 16:19
-
-
Save noonien/c09d49d189c97070f40774c1e230d93f to your computer and use it in GitHub Desktop.
LRU cache for stat()
This file contains hidden or 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
// compile with: g++ -fPIC -shared -std=c++11 cache_stat.cpp -ldl -O3 -o cache_stat.so | |
#include <functional> | |
#include <sys/stat.h> | |
#include <dlfcn.h> | |
#include "lrucache.hpp" | |
typedef int (*xstat_type)(int ver, const char *pathname, struct stat *buf); | |
typedef int (*xstat64_type)(int ver, const char *pathname, struct stat64 *buf); | |
typedef int (*fxstatat_type)(int ver, int dirfd, const char *path, struct stat *stat_buf, int flags); | |
xstat_type _orig_xstat; | |
xstat64_type _orig_xstat64; | |
fxstatat_type _orig_fxstatat; | |
void _loaded() __attribute__((constructor)); | |
void _loaded() { | |
_orig_xstat = (xstat_type)dlsym(RTLD_NEXT, "__xstat"); | |
_orig_xstat64 = (xstat64_type)dlsym(RTLD_NEXT, "__xstat64"); | |
_orig_fxstatat = (fxstatat_type)dlsym(RTLD_NEXT, "__fxstatat"); | |
} | |
struct _res { | |
int ret; | |
struct stat buf; | |
}; | |
struct _res64 { | |
int ret; | |
struct stat64 buf; | |
}; | |
struct _fxstatat_key { | |
int dirfd; | |
std::string path; | |
int flags; | |
}; | |
bool operator==(const _fxstatat_key& lhs, const _fxstatat_key& rhs) | |
{ | |
return lhs.dirfd == rhs.dirfd && lhs.path == rhs.path && lhs.flags == rhs.flags; | |
} | |
namespace std { | |
template <> | |
struct hash<struct _fxstatat_key> { | |
size_t operator()(const struct _fxstatat_key &k) const { | |
return k.dirfd ^ k.flags ^ hash<string>()(k.path); | |
} | |
}; | |
} | |
const int CACHE_ENTRIES = 2000; | |
cache::lru_cache<std::string, struct _res*> lru_xstat(CACHE_ENTRIES); | |
cache::lru_cache<std::string, struct _res64*> lru_xstat64(CACHE_ENTRIES); | |
cache::lru_cache<_fxstatat_key, struct _res*> lru_fxstatat(CACHE_ENTRIES); | |
extern "C" int __xstat(int ver, const char *pathname, struct stat *buf) { | |
auto key = std::string(pathname); | |
struct _res *res; | |
if (lru_xstat.exists(key)) { | |
res = lru_xstat.get(key); | |
*buf = res->buf; | |
return res->ret; | |
} | |
res = new _res; | |
res->ret = _orig_xstat(ver, pathname, &res->buf); | |
*buf = res->buf; | |
lru_xstat.put(key, res); | |
return res->ret; | |
} | |
extern "C" int __xstat64(int ver, const char *pathname, struct stat64 *buf) { | |
auto key = std::string(pathname); | |
struct _res64 *res; | |
if (lru_xstat64.exists(key)) { | |
res = lru_xstat64.get(key); | |
*buf = res->buf; | |
return res->ret; | |
} | |
res = new _res64; | |
res->ret = _orig_xstat64(ver, pathname, &res->buf); | |
*buf = res->buf; | |
lru_xstat64.put(key, res); | |
return res->ret; | |
} | |
extern "C" int __fxstatat(int ver, int dirfd, const char *path, struct stat *stat_buf, int flags) { | |
struct _fxstatat_key key{dirfd, std::string(path)}; | |
struct _res *res; | |
if (lru_fxstatat.exists(key)) { | |
res = lru_fxstatat.get(key); | |
*stat_buf = res->buf; | |
return res->ret; | |
} | |
res = new _res; | |
res->ret = _orig_fxstatat(ver, dirfd, path, &res->buf, flags); | |
*stat_buf = res->buf; | |
lru_fxstatat.put(key, res); | |
return res->ret; | |
} |
This file contains hidden or 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
/* | |
* File: lrucache.hpp | |
* Author: Alexander Ponomarev | |
* | |
* Created on June 20, 2013, 5:09 PM | |
*/ | |
#ifndef _LRUCACHE_HPP_INCLUDED_ | |
#define _LRUCACHE_HPP_INCLUDED_ | |
#include <unordered_map> | |
#include <list> | |
#include <cstddef> | |
#include <stdexcept> | |
namespace cache { | |
template<typename key_t, typename value_t> | |
class lru_cache { | |
public: | |
typedef typename std::pair<key_t, value_t> key_value_pair_t; | |
typedef typename std::list<key_value_pair_t>::iterator list_iterator_t; | |
lru_cache(size_t max_size) : | |
_max_size(max_size) { | |
} | |
void put(const key_t& key, const value_t& value) { | |
auto it = _cache_items_map.find(key); | |
_cache_items_list.push_front(key_value_pair_t(key, value)); | |
if (it != _cache_items_map.end()) { | |
_cache_items_list.erase(it->second); | |
_cache_items_map.erase(it); | |
} | |
_cache_items_map[key] = _cache_items_list.begin(); | |
if (_cache_items_map.size() > _max_size) { | |
auto last = _cache_items_list.end(); | |
last--; | |
_cache_items_map.erase(last->first); | |
_cache_items_list.pop_back(); | |
} | |
} | |
const value_t& get(const key_t& key) { | |
auto it = _cache_items_map.find(key); | |
if (it == _cache_items_map.end()) { | |
throw std::range_error("There is no such key in cache"); | |
} else { | |
_cache_items_list.splice(_cache_items_list.begin(), _cache_items_list, it->second); | |
return it->second->second; | |
} | |
} | |
bool exists(const key_t& key) const { | |
return _cache_items_map.find(key) != _cache_items_map.end(); | |
} | |
size_t size() const { | |
return _cache_items_map.size(); | |
} | |
private: | |
std::list<key_value_pair_t> _cache_items_list; | |
std::unordered_map<key_t, list_iterator_t> _cache_items_map; | |
size_t _max_size; | |
}; | |
} // namespace lru | |
#endif /* _LRUCACHE_HPP_INCLUDED_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment