Skip to content

Instantly share code, notes, and snippets.

@noonien
Last active October 13, 2016 16:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save noonien/c09d49d189c97070f40774c1e230d93f to your computer and use it in GitHub Desktop.
Save noonien/c09d49d189c97070f40774c1e230d93f to your computer and use it in GitHub Desktop.
LRU cache for stat()
// 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;
}
/*
* 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