Cachelot
キャッシュライブラリ
Cachelot
概要
CachelotはC++で書かれた高性能なLRUキャッシュライブラリおよびMemcached互換の分散キャッシュサーバーで、メモリ効率性と高速性を重視した設計になっています。
詳細
CachelotはBSD 2-Clauseライセンスで提供される無料のオープンソースC++キャッシュライブラリで、高速動作が要求されるアプリケーション向けのLRUキャッシュ機能を提供します。固定メモリ量での動作を前提とし、ガベージコレクタを使わずに95-98%という非常に高いメモリ利用率を実現します。シングルスレッド設計により、ロックや無駄なRAMアクセスでCPUサイクルを浪費することなく、CPUの99%を有効活用できます。また、1024コアまでのスケーラビリティを持ちます。IoTデバイスやハンドヘルドデバイスのようなリソース制限環境から、大容量RAMを持つサーバーまで幅広く対応可能です。キャッシュライブラリとしてだけでなく、Memcached互換のcachelotdサーバーも提供し、Memcachedよりも15%のRAM節約と高速なレスポンスを実現します。
メリット・デメリット
メリット
- 超高性能: 最適化されたC++実装による極めて高速な動作
- メモリ効率: 95-98%の高いメモリ利用率と5-7%の低いオーバーヘッド
- スケーラビリティ: 1024コアまでのスケーラブル設計
- シングルスレッド: ロック競合なしで安定したパフォーマンス
- クロスプラットフォーム: Linux、macOS、Windows対応
- Memcached互換: 既存のMemcachedクライアントと互換性
- 軽量: IoTデバイスから大規模サーバーまで対応
デメリット
- C++専用: 他の言語からの直接利用には言語バインディングが必要
- 学習コスト: C++の知識とメモリ管理の理解が必要
- 複雑性: シングルスレッド設計による実装の複雑さ
- 新しいプロジェクト: 比較的新しく、実績が限定的
- ドキュメント: 大手プロジェクトと比較してドキュメントが少ない
- デバッグ: 低レベル実装によるデバッグの難しさ
主要リンク
書き方の例
基本的なLRUキャッシュ使用
#include <cachelot/cache.h>
#include <iostream>
#include <string>
int main() {
// 1MB のキャッシュを作成
cachelot::Cache cache(1024 * 1024);
// データの保存
std::string key = "user:123";
std::string value = "John Doe";
cache.set(key, value);
// データの取得
auto result = cache.get(key);
if (result) {
std::cout << "キャッシュヒット: " << *result << std::endl;
} else {
std::cout << "キャッシュミス" << std::endl;
}
return 0;
}
有効期限付きキャッシュ
#include <cachelot/cache.h>
#include <chrono>
void expiration_example() {
cachelot::Cache cache(1024 * 1024);
// 30秒の有効期限でデータを保存
std::string key = "session:abc123";
std::string session_data = "user_session_data";
auto expire_time = std::chrono::steady_clock::now() +
std::chrono::seconds(30);
cache.set(key, session_data, expire_time);
// 即座に取得(有効)
auto result1 = cache.get(key);
if (result1) {
std::cout << "セッションが有効: " << *result1 << std::endl;
}
// 30秒後には自動的に削除される
}
カスタムキーの使用
#include <cachelot/cache.h>
#include <vector>
// カスタムキー型の定義
struct UserKey {
int user_id;
std::string region;
bool operator==(const UserKey& other) const {
return user_id == other.user_id && region == other.region;
}
};
// カスタムハッシュ関数
namespace std {
template<>
struct hash<UserKey> {
size_t operator()(const UserKey& key) const {
return hash<int>()(key.user_id) ^
hash<string>()(key.region);
}
};
}
void custom_key_example() {
cachelot::Cache cache(2 * 1024 * 1024); // 2MB
UserKey key{123, "US"};
std::string user_data = "John Doe from US";
cache.set(key, user_data);
auto result = cache.get(key);
if (result) {
std::cout << "ユーザーデータ: " << *result << std::endl;
}
}
統計情報の取得
#include <cachelot/cache.h>
void statistics_example() {
cachelot::Cache cache(1024 * 1024);
// 複数のデータ操作
for (int i = 0; i < 100; ++i) {
std::string key = "key_" + std::to_string(i);
std::string value = "value_" + std::to_string(i);
cache.set(key, value);
}
// いくつかのキーを取得してヒット率を生成
for (int i = 0; i < 50; ++i) {
std::string key = "key_" + std::to_string(i);
auto result = cache.get(key);
}
// 存在しないキーでミスを生成
for (int i = 200; i < 220; ++i) {
std::string key = "missing_key_" + std::to_string(i);
auto result = cache.get(key);
}
// 統計情報を取得
auto stats = cache.get_stats();
std::cout << "ヒット数: " << stats.hits << std::endl;
std::cout << "ミス数: " << stats.misses << std::endl;
std::cout << "アイテム数: " << stats.items << std::endl;
std::cout << "使用メモリ: " << stats.memory_used << " bytes" << std::endl;
}
バイナリデータのキャッシュ
#include <cachelot/cache.h>
#include <vector>
#include <cstring>
void binary_data_example() {
cachelot::Cache cache(1024 * 1024);
// 画像データなどのバイナリデータをシミュレート
std::vector<uint8_t> image_data(1024, 0xFF); // 1KB の画像データ
std::string image_key = "image:profile_123.jpg";
// バイナリデータをキャッシュに保存
cache.set(image_key, image_data);
// バイナリデータを取得
auto result = cache.get(image_key);
if (result) {
const auto& cached_data = *result;
std::cout << "画像データサイズ: " << cached_data.size()
<< " bytes" << std::endl;
// データの整合性チェック
if (cached_data == image_data) {
std::cout << "データの整合性OK" << std::endl;
}
}
}
マルチタイプキャッシュ
#include <cachelot/cache.h>
#include <variant>
#include <string>
// 複数の型を格納できるキャッシュ値
using CacheValue = std::variant<std::string, int, double, std::vector<int>>;
class MultiTypeCache {
private:
cachelot::Cache cache_;
public:
MultiTypeCache(size_t size) : cache_(size) {}
template<typename T>
void set(const std::string& key, const T& value) {
CacheValue cache_value = value;
cache_.set(key, cache_value);
}
template<typename T>
std::optional<T> get(const std::string& key) {
auto result = cache_.get(key);
if (result) {
const auto& cache_value = *result;
if (std::holds_alternative<T>(cache_value)) {
return std::get<T>(cache_value);
}
}
return std::nullopt;
}
};
void multi_type_example() {
MultiTypeCache cache(1024 * 1024);
// 異なる型のデータを保存
cache.set("string_key", std::string("Hello World"));
cache.set("int_key", 42);
cache.set("double_key", 3.14159);
cache.set("vector_key", std::vector<int>{1, 2, 3, 4, 5});
// データを取得
auto str_val = cache.get<std::string>("string_key");
auto int_val = cache.get<int>("int_key");
auto double_val = cache.get<double>("double_key");
auto vector_val = cache.get<std::vector<int>>("vector_key");
if (str_val) std::cout << "文字列: " << *str_val << std::endl;
if (int_val) std::cout << "整数: " << *int_val << std::endl;
if (double_val) std::cout << "実数: " << *double_val << std::endl;
if (vector_val) {
std::cout << "ベクター: ";
for (const auto& item : *vector_val) {
std::cout << item << " ";
}
std::cout << std::endl;
}
}