Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, macos-14]
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ['3.10', '3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@v4
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,30 @@ reader = lcs.TraceReader(
)

# Step 2: Initialize cache
# Note: cache_size as float (0.1) means 10% of the reader's total working set size in bytes.
# To specify an absolute size, pass an integer (e.g., 1024*1024 for 1MB).
cache = lcs.S3FIFO(
cache_size=1024*1024,
cache_size=0.1, # 0.1 = 10% of trace's working set size (requires reader parameter)
# Cache specific parameters
small_size_ratio=0.2,
ghost_size_ratio=0.8,
move_to_main_threshold=2,
reader=reader, # Required when cache_size is a float ratio
)

# Step 3: Process entire trace efficiently (C++ backend)
req_miss_ratio, byte_miss_ratio = cache.process_trace(reader)
print(f"Request miss ratio: {req_miss_ratio:.4f}, Byte miss ratio: {byte_miss_ratio:.4f}")

# Step 3.1: Process the first 1000 requests
# Note: cache_size as float means a ratio of the working set size (requires reader parameter)
cache = lcs.S3FIFO(
cache_size=1024 * 1024,
cache_size=0.1, # 10% of trace's working set size
# Cache specific parameters
small_size_ratio=0.2,
ghost_size_ratio=0.8,
move_to_main_threshold=2,
reader=reader, # Required when cache_size is a float ratio
)
req_miss_ratio, byte_miss_ratio = cache.process_trace(reader, start_req=0, max_req=1000)
print(f"Request miss ratio: {req_miss_ratio:.4f}, Byte miss ratio: {byte_miss_ratio:.4f}")
Expand Down
216 changes: 190 additions & 26 deletions libcachesim/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import annotations
from typing import bool, int, str, tuple
from typing import Optional, Callable, Any
from collections.abc import Iterator

from .libcachesim_python import ReqOp, TraceType, SamplerType
Expand Down Expand Up @@ -38,6 +38,85 @@ class CommonCacheParams:
hashpower: int
consider_obj_metadata: bool

class ReaderInitParam:
ignore_obj_size: bool
ignore_size_zero_req: bool
obj_id_is_num: bool
obj_id_is_num_set: bool
cap_at_n_req: int
time_field: int
obj_id_field: int
obj_size_field: int
op_field: int
ttl_field: int
cnt_field: int
tenant_field: int
next_access_vtime_field: int
n_feature_fields: int
block_size: int
has_header: bool
has_header_set: bool
delimiter: str
trace_start_offset: int
binary_fmt_str: str
def __init__(
self,
binary_fmt_str: str = "",
ignore_obj_size: bool = False,
ignore_size_zero_req: bool = True,
obj_id_is_num: bool = True,
obj_id_is_num_set: bool = False,
cap_at_n_req: int = -1,
block_size: int = -1,
has_header: bool = False,
has_header_set: bool = False,
delimiter: str = ",",
trace_start_offset: int = 0,
sampler: Optional[Any] = None,
): ...

class AnalysisParam:
access_pattern_sample_ratio_inv: int
track_n_popular: int
track_n_hit: int
time_window: int
warmup_time: int
def __init__(
self,
access_pattern_sample_ratio_inv: int = 10,
track_n_popular: int = 10,
track_n_hit: int = 5,
time_window: int = 60,
warmup_time: int = 0,
): ...

class AnalysisOption:
req_rate: bool
access_pattern: bool
size: bool
reuse: bool
popularity: bool
ttl: bool
popularity_decay: bool
lifetime: bool
create_future_reuse_ccdf: bool
prob_at_age: bool
size_change: bool
def __init__(
self,
req_rate: bool = True,
access_pattern: bool = True,
size: bool = True,
reuse: bool = True,
popularity: bool = True,
ttl: bool = False,
popularity_decay: bool = False,
lifetime: bool = False,
create_future_reuse_ccdf: bool = False,
prob_at_age: bool = False,
size_change: bool = False,
): ...

class Cache:
cache_size: int
default_ttl: int
Expand Down Expand Up @@ -80,113 +159,164 @@ class CacheBase:
def cache_name(self) -> str: ...

# Core cache algorithms
class LHD(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class LRU(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class FIFO(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class LFU(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class ARC(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class Clock(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, init_freq: int = 0, n_bit_counter: int = 1, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class Random(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

# Advanced algorithms
class S3FIFO(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, small_size_ratio: float = 0.1, ghost_size_ratio: float = 0.9, move_to_main_threshold: int = 2, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class Sieve(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class LIRS(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class TwoQ(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, a_in_size_ratio: float = 0.25, a_out_size_ratio: float = 0.5, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class SLRU(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class WTinyLFU(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, main_cache: str = "SLRU", window_size: float = 0.01, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class LeCaR(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, update_weight: bool = True, lru_weight: float = 0.5, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class LFUDA(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class ClockPro(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, init_ref: int = 0, init_ratio_cold: float = 0.5, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class Cacheus(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

# Optimal algorithms
class Belady(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class BeladySize(CacheBase):
def __init__(
self, cache_size: int, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, n_samples: int = 128, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

# Probabilistic algorithms
class LRUProb(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, prob: float = 0.5, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class FlashProb(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, ram_size_ratio: float = 0.05, disk_admit_prob: float = 0.2, ram_cache: str = "LRU", disk_cache: str = "FIFO", admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

# Size-based algorithms
class Size(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class GDSF(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

# Hyperbolic algorithms
class Hyperbolic(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

# Extra deps
class ThreeLCache(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, objective: str = "byte-miss-ratio", admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class GLCache(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, segment_size: int = 100, n_merge: int = 2, type: str = "learned", rank_intvl: float = 0.02, merge_consecutive_segs: bool = True, train_source_y: str = "online", retrain_intvl: int = 86400, admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

class LRB(CacheBase):
def __init__(
self, cache_size: int | float, default_ttl: int = 25920000, hashpower: int = 24, consider_obj_metadata: bool = False, objective: str = "byte-miss-ratio", admissioner: Optional["AdmissionerBase"] = None, reader: Optional[ReaderProtocol] = None
): ...

# Plugin cache
class PluginCache(CacheBase):
def __init__(
self,
cache_size: int,
cache_size: int | float,
cache_init_hook: Callable,
cache_hit_hook: Callable,
cache_miss_hook: Callable,
cache_eviction_hook: Callable,
cache_remove_hook: Callable,
cache_free_hook: Optional[Callable] = None,
cache_name: str = "PythonHookCache",
default_ttl: int = 25920000,
hashpower: int = 24,
consider_obj_metadata: bool = False,
cache_init_hook=None,
cache_hit_hook=None,
cache_miss_hook=None,
cache_eviction_hook=None,
cache_remove_hook=None,
cache_free_hook=None,
admissioner: Optional["AdmissionerBase"] = None,
reader: Optional[ReaderProtocol] = None,
): ...
def set_hooks(self, init_hook, hit_hook, miss_hook, eviction_hook, remove_hook, free_hook=None): ...

Expand Down Expand Up @@ -246,3 +376,37 @@ class Util:
def process_trace(
cache: CacheBase, reader: ReaderProtocol, start_req: int = 0, max_req: int = -1
) -> tuple[float, float]: ...

# Admissioners
class AdmissionerBase:
def __init__(self, _admissioner): ...
def clone(self): ...
def update(self, req: Request, cache_size: int): ...
def admit(self, req: Request) -> bool: ...
def free(self): ...

class BloomFilterAdmissioner(AdmissionerBase):
def __init__(self): ...

class ProbAdmissioner(AdmissionerBase):
def __init__(self, prob: Optional[float] = None): ...

class SizeAdmissioner(AdmissionerBase):
def __init__(self, size_threshold: Optional[int] = None): ...

class SizeProbabilisticAdmissioner(AdmissionerBase):
def __init__(self, exponent: Optional[float] = None): ...

class AdaptSizeAdmissioner(AdmissionerBase):
def __init__(self, max_iteration: Optional[int] = None, reconf_interval: Optional[int] = None): ...

class PluginAdmissioner(AdmissionerBase):
def __init__(
self,
admissioner_name: str,
admissioner_init_hook: Callable,
admissioner_admit_hook: Callable,
admissioner_clone_hook: Callable,
admissioner_update_hook: Callable,
admissioner_free_hook: Callable,
): ...
Loading
Loading