diff --git a/CMakeLists.txt b/CMakeLists.txt index 2eba5b37..f1fff60c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option(SUPPORT_TTL "whether support TTL" OFF) option(OPT_SUPPORT_ZSTD_TRACE "whether support zstd trace" ON) option(ENABLE_LRB "enable LRB" OFF) option(ENABLE_3L_CACHE "enable 3LCache" OFF) +option(BUILD_SHARED_LIBS "Build shared library" ON) set(LOG_LEVEL NONE CACHE STRING "change the logging level") set_property(CACHE LOG_LEVEL PROPERTY STRINGS INFO WARN ERROR DEBUG VERBOSE VVERBOSE VVVERBOSE) @@ -354,9 +355,12 @@ set(LIB_SOURCE ${LIB_SOURCE} ${cache_source} ${reader_source} ${dataStructure_so # # https://stackoverflow.com/questions/32469953/why-is-cmake-designed-so-that-it-removes-runtime-path-when-installing SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -add_library(${PROJECT_NAME} ${LIB_SOURCE}) +if(BUILD_SHARED_LIBS) + add_library(${PROJECT_NAME} SHARED ${LIB_SOURCE}) +else() + add_library(${PROJECT_NAME} ${LIB_SOURCE}) +endif() -# add_library(${PROJECT_NAME} SHARED ${LIB_SOURCE}) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION}) set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C) set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/libCacheSim/include/libCacheSim.h) @@ -383,3 +387,8 @@ if(ENABLE_TESTS) else() message(STATUS "Building without test") endif() + +if (BUILD_SHARED_LIBS) + target_link_libraries(${PROJECT_NAME} PUBLIC ${LIBS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libCacheSim/python) +endif() diff --git a/libCacheSim/bin/cachesim/CMakeLists.txt b/libCacheSim/bin/cachesim/CMakeLists.txt index 3c872b0e..db8b9561 100644 --- a/libCacheSim/bin/cachesim/CMakeLists.txt +++ b/libCacheSim/bin/cachesim/CMakeLists.txt @@ -3,5 +3,8 @@ add_executable(cachesim main.c cli_parser.c sim.c ../cli_reader_utils.c) target_link_libraries(cachesim ${ALL_MODULES} ${LIBS} ${CMAKE_THREAD_LIBS_INIT} utils) install(TARGETS cachesim RUNTIME DESTINATION bin) -# add_executable(flash flash.cpp cli_parser.c sim.c ../cli_reader_utils.c) -# target_link_libraries(flash ${ALL_MODULES} ${LIBS} ${CMAKE_THREAD_LIBS_INIT} utils) +if (BUILD_SHARED_LIBS) + set(source cli_parser.c sim.c ../cli_reader_utils.c) + add_library(lcachesim ${source}) + target_link_libraries(lcachesim ${ALL_MODULES} ${LIBS} ${CMAKE_THREAD_LIBS_INIT} utils) +endif() \ No newline at end of file diff --git a/libCacheSim/bin/cachesim/cache_init.h b/libCacheSim/bin/cachesim/cache_init.h index 66874a5c..2ba0e460 100644 --- a/libCacheSim/bin/cachesim/cache_init.h +++ b/libCacheSim/bin/cachesim/cache_init.h @@ -1,6 +1,8 @@ #include +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include @@ -72,7 +74,7 @@ static inline cache_t *create_cache(const char *trace_path, const char *eviction } else { const char *window_size = strstr(eviction_params, "window-size="); if (window_size == NULL) { - char *new_params = malloc(strlen(eviction_params) + 20); + char *new_params = (char *)malloc(strlen(eviction_params) + 20); sprintf(new_params, "%s,window-size=0.01", eviction_params); cache = WTinyLFU_init(cc_params, new_params); } else { diff --git a/libCacheSim/include/python/api.h b/libCacheSim/include/python/api.h new file mode 100644 index 00000000..8a7a83b7 --- /dev/null +++ b/libCacheSim/include/python/api.h @@ -0,0 +1,115 @@ +#ifndef LIBCACHESIM_API_H_ +#define LIBCACHESIM_API_H_ + +// TODO(haocheng): add more headers +#include "../../bin/cachesim/cache_init.h" +#include "../../bin/cli_reader_utils.h" +#include "../libCacheSim/simulator.h" + +#ifdef __cplusplus +#include +#include +#include +#define LIBCACHESIM_C_EXPORT extern "C" +#else +#include +#include +#include +#define LIBCACHESIM_C_EXPORT +#endif + +typedef void* CacheHandle; /*!< \brief Handle of cache. */ +typedef void* ReaderHandle; /*!< \brief Handle of reader. */ +typedef void* CacheObjHandle; /*!< \brief Handle of cache object. */ +typedef void* RequestHandle; /*!< \brief Handle of request. */ + +#define C_API_DTYPE_FLOAT32 (0) /*!< \brief float32 (single precision float). */ +#define C_API_DTYPE_FLOAT64 (1) /*!< \brief float64 (double precision float). */ +#define C_API_DTYPE_INT32 (2) /*!< \brief int32. */ +#define C_API_DTYPE_INT64 (3) /*!< \brief int64. */ + +LIBCACHESIM_C_EXPORT int LCS_Simulate(const char* trace_path, const char* trace_type_str, const char* eviction_algo, + float cache_size, bool ignore_obj_size, const char* eviction_params, + int64_t* n_req, // TODO(haocheng): add more results + int64_t* n_miss); + +/* --- start Cache interface */ +/*! + * \brief Create a reader from a trace file. + * \param filename The name of the trace file + * \param parameters Additional parameters + * \param[out] out A reader + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_ReaderCreateFromFile(const char* filename, const char* parameters, + ReaderHandle* out); + +/*! + * \brief Free a reader. + * \param reader The reader + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_ReaderFree(const ReaderHandle reader); + +/*! + * \brief Get the next request from the reader. + * \param reader The reader + * \param[out] out A request + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_ReaderGetNextRequest(const ReaderHandle reader, RequestHandle* out); + +/*! + * \brief Get the number of requests in the reader. + * \param reader The reader + * \param[out] out The number of requests + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_ReaderGetReqNum(const ReaderHandle reader, int64_t* out); + +/*! + * \brief Get the number of objects in the reader. + * \param reader The reader + * \param[out] out The number of objects + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_ReaderGetObjNum(const ReaderHandle reader, int64_t* out); + +/*! + * \brief Free a request. + * \param request The request + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_RequestFree(const RequestHandle request); + +/*! + * \brief Create a cache. + * \param reader The reader + * \param eviction_algo The name of the eviction algorithm + * \param cache_size The size of the cache + * \param ignore_obj_size Whether to ignore the object size + * \param eviction_parameters Additional parameters + * \param[out] out A cache + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_CacheCreate(const ReaderHandle reader, const char* eviction_algo, float cache_size, bool ignore_obj_size, + const char* eviction_parameters, CacheHandle* out); + +/*! + * \brief Free a cache. + * \param cache The cache + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_CacheFree(const CacheHandle cache); + +/*! + * \brief Get a cache object. + * \param cache The cache + * \param request The request + * \param[out] hit Whether the request is hit + * \return 0 when succeed, -1 when failure happens + */ +LIBCACHESIM_C_EXPORT int LCS_CacheGet(const CacheHandle cache, const RequestHandle request, + bool* hit); + +#endif // LIBCACHESIM_API_H_ \ No newline at end of file diff --git a/libCacheSim/python/CMakeLists.txt b/libCacheSim/python/CMakeLists.txt new file mode 100644 index 00000000..fe875c1e --- /dev/null +++ b/libCacheSim/python/CMakeLists.txt @@ -0,0 +1,3 @@ +aux_source_directory(. source) +add_library (python ${source}) +target_link_libraries(python lcachesim) \ No newline at end of file diff --git a/libCacheSim/python/api.cpp b/libCacheSim/python/api.cpp new file mode 100644 index 00000000..b884aabd --- /dev/null +++ b/libCacheSim/python/api.cpp @@ -0,0 +1,169 @@ +#include "../include/python/api.h" + +#include +#include + +// namespace libCacheSim { + +#define API_BEGIN() try { +#define API_END() \ + } \ + catch (std::exception & ex) { \ + return LCS_APIHandleException(ex.what()); \ + } \ + catch (...) { \ + return LCS_APIHandleException("unknown exception"); \ + } \ + return 0; + +inline int LCS_APIHandleException(const char* ex) { + // dump exception + std::cerr << ex << std::endl; + return -1; +} + +uint64_t _convert_cache_size_to_bytes(float cache_size, ReaderHandle reader, bool ignore_obj_size) { + int64_t wss_obj = 0, wss_byte = 0; + cal_working_set_size(static_cast(reader), &wss_obj, &wss_byte); + uint64_t wss = ignore_obj_size ? wss_obj : wss_byte; + uint64_t cache_size_bytes = 0; + if (cache_size <= 0) { + cache_size_bytes = wss; + } else if (cache_size < 1) { + cache_size_bytes = (uint64_t)(wss * cache_size); + } else { + cache_size_bytes = (uint64_t)cache_size; + } + return cache_size_bytes; +} + +int LCS_Simulate(const char* trace_path, const char* trace_type_str, const char* eviction_algo, float cache_size, + bool ignore_obj_size, const char* eviction_params, + // results + int64_t* n_req, // TODO(haocheng): add more results + int64_t* n_miss) { + API_BEGIN(); + trace_type_e trace_type = trace_type_str_to_enum(trace_type_str, trace_path); + reader_t* reader = setup_reader(trace_path, trace_type, NULL); + uint64_t cache_size_bytes = _convert_cache_size_to_bytes(cache_size, reader, ignore_obj_size); + + // TODO(haocheng): consider obj metadata is false by default, make it changeable + cache_t* cache = create_cache(trace_path, eviction_algo, cache_size_bytes, eviction_params, false); + + int num_of_sizes = 1; + uint64_t cache_sizes[1] = {cache_size_bytes}; + reader_t* warmup_reader = NULL; + double warmup_frac = 0; + int warmup_sec = 0; + int num_of_threads = 1; + bool use_random_seed = false; + // simulate + cache_stat_t* stat = simulate_at_multi_sizes(reader, cache, num_of_sizes, cache_sizes, warmup_reader, warmup_frac, + warmup_sec, num_of_threads, use_random_seed); + // extract results + *n_req = stat[0].n_req; + *n_miss = stat[0].n_miss; + API_END(); + return 0; +} + +// ---------------------------------------------------------------------------- +char* _extract_trace_type_str(const char* trace_path) { + // TODO(haocheng): add more trace types + // 'csv', 'oracleGeneral', 'vscsi', 'txt' + // match string in trace_path + if (strstr(trace_path, "csv")) { + return "csv"; + } else if (strstr(trace_path, "oracleGeneral")) { + return "oracleGeneral"; + } else if (strstr(trace_path, "vscsi")) { + return "vscsi"; + } else if (strstr(trace_path, "txt")) { + return "txt"; + } else { + // exception + throw LCS_APIHandleException("Unknown trace type"); + } +} + +int LCS_ReaderCreateFromFile(const char* filename, const char* parameters, ReaderHandle* out) { + API_BEGIN(); + // TODO(haocheng): add more parameters + // initialize reader by filename + trace_type_e trace_type = trace_type_str_to_enum(_extract_trace_type_str(filename), filename); + reader_t* reader = setup_reader(filename, trace_type, NULL); + if (reader == NULL) { + printf("Failed to create reader\n"); + } + *out = (ReaderHandle)reader; + API_END(); +} + +int LCS_ReaderFree(const ReaderHandle reader) { + API_BEGIN(); + close_reader(static_cast(reader)); + API_END(); +} + +int LCS_ReaderGetNextRequest(const ReaderHandle reader, RequestHandle* out) { + API_BEGIN(); + request_t* request = (request_t*)malloc(sizeof(request_t)); + int status = read_one_req(static_cast(reader), request); + if (status == 0) { + *out = request; + } else if (status == 1) { + *out = NULL; + } else { + printf("Failed to get next request\n"); + return -1; + } + API_END(); +} + +int LCS_ReaderGetObjNum(const ReaderHandle reader, int64_t* out) { + API_BEGIN(); + int64_t wss_obj = 0, wss_byte = 0; + cal_working_set_size(static_cast(reader), &wss_obj, &wss_byte); + *out = wss_obj; + API_END(); +} + +int LCS_ReaderGetReqNum(const ReaderHandle reader, int64_t* out) { + API_BEGIN(); + *out = (int64_t)(static_cast(reader)->n_total_req); + API_END(); +} + +int LCS_CacheCreate(const ReaderHandle reader, const char* eviction_algo, float cache_size, bool ignore_obj_size, + const char* eviction_parameters, CacheHandle* out) { + API_BEGIN(); + // TODO(haocheng): add more parameters + const char* trace_path = static_cast(reader)->trace_path; + cache_t* cache = + create_cache(trace_path, eviction_algo, _convert_cache_size_to_bytes(cache_size, reader, ignore_obj_size), + eviction_parameters, false); + *out = cache; + API_END(); +} + +int LCS_CacheFree(const CacheHandle cache) { + API_BEGIN(); + cache_t* cache_ptr = static_cast(cache); + cache_ptr->cache_free(cache_ptr); + API_END(); +} + +int LCS_CacheGet(const CacheHandle cache, const RequestHandle request, bool* hit) { + API_BEGIN(); + *hit = cache_get_base(static_cast(cache), static_cast(request)); + // printf("hit from c side: %d\n", *hit); + API_END(); +} + +int LCS_RequestFree(const RequestHandle request) { + API_BEGIN(); + free_request(static_cast(request)); + API_END(); +} + +// } \ No newline at end of file diff --git a/libCacheSim/python/design.md b/libCacheSim/python/design.md new file mode 100644 index 00000000..120a6381 --- /dev/null +++ b/libCacheSim/python/design.md @@ -0,0 +1,7 @@ +Expose the cache interface to the user. + +```python +cache = FIFO(cache_size, **kwargs) +for req in reader: + hit = cache.get(req) +``` \ No newline at end of file diff --git a/python-package/README.md b/python-package/README.md new file mode 100644 index 00000000..0c69323b --- /dev/null +++ b/python-package/README.md @@ -0,0 +1,18 @@ +libCacheSim Python-package +======================= + +## Installation + +### Install from [PyPI](https://pypi.org/project/libCacheSim) + +note: coming soon + +```sh +pip install libcachesim +``` + +### Build from Sources + +```sh +pip install --no-binary libcachesim libcachesim +``` diff --git a/python-package/example_cache.py b/python-package/example_cache.py new file mode 100644 index 00000000..9ea35e98 --- /dev/null +++ b/python-package/example_cache.py @@ -0,0 +1,16 @@ +from libcachesim import simulate, FIFO, Reader + +reader = Reader(b"../data/cloudPhysicsIO.vscsi") +print("req num: ", reader.get_req_num()) +print("obj num: ", reader.get_obj_num()) + +cache = FIFO(reader, cache_size=0.1, ignore_obj_size=False) + +hit_cnt = 0 +miss_cnt = 0 +for req in reader: + hit = cache.get(req) + hit_cnt += 1 if hit else 0 + miss_cnt += 1 if not hit else 0 + +print(hit_cnt, miss_cnt) \ No newline at end of file diff --git a/python-package/example_sim.py b/python-package/example_sim.py new file mode 100644 index 00000000..45dc4f15 --- /dev/null +++ b/python-package/example_sim.py @@ -0,0 +1,5 @@ +from libcachesim import simulate + +req, miss = simulate(b"../data/cloudPhysicsIO.vscsi", b"vscsi", b"fifo", 0.1) +print(req, miss) +print(miss / req) \ No newline at end of file diff --git a/python-package/libcachesim/__init__.py b/python-package/libcachesim/__init__.py new file mode 100644 index 00000000..0a53c92d --- /dev/null +++ b/python-package/libcachesim/__init__.py @@ -0,0 +1,26 @@ +# coding: utf-8 +"""libCacheSim, Cache Simulation Library. + +Contributors: https://github.com/1a1a11a/libCacheSim/graphs/contributors. +""" + +from pathlib import Path + +# .basic is intentionally loaded as early as possible, to dlopen() libcachesim.{dll,dylib,so} +# and its dependencies as early as possible +from .basic import simulate, FIFO, Reader, Request, Cache + + +# _version_path = Path(__file__).absolute().parent / "VERSION.txt" +# if _version_path.is_file(): +# __version__ = _version_path.read_text(encoding="utf-8").strip() +__version__ = "0.1.0" + +__all__ = [ + "simulate", + "Reader", + "Request", + "Cache", + # eviction algorithms + "FIFO", +] diff --git a/python-package/libcachesim/basic.py b/python-package/libcachesim/basic.py new file mode 100644 index 00000000..ec00c4ce --- /dev/null +++ b/python-package/libcachesim/basic.py @@ -0,0 +1,178 @@ +# coding: utf-8 +"""Wrapper for C API of libCacheSim.""" + +# This import causes liblibcachesim.{dll,dylib,so} to be loaded. +from .libpath import _LIB +import ctypes +from typing import Optional, Union, Dict, Any +from pathlib import Path + + +_ReaderHandle = ctypes.c_void_p +_CacheHandle = ctypes.c_void_p +_ReqHandle = ctypes.c_void_p + + +def _safe_call(ret: int) -> None: + """Check the return value from C API call. + + Parameters + ---------- + ret : int + The return value from C API calls. + """ + if ret != 0: + # TODO(haocheng):add error message from C API + raise Exception(f"C API call failed with error code {ret}") + +# ----------------------------------------------------------------------------- +# Simulation API +# ----------------------------------------------------------------------------- +def simulate(trace_path: str, trace_type_str: str, eviction_algo: str, cache_size: float, + eviction_params: Optional[str]=None, consider_obj_metadata: bool=False) -> float: + """Simulate the cache performance. + Parameters + ---------- + trace_path : str + The path to the trace file. + eviction_algo : str + The eviction algorithm. + + """ + cache_size = ctypes.c_float(cache_size) + n_req = ctypes.c_int64(0) + n_miss = ctypes.c_int64(0) + _safe_call(_LIB.LCS_Simulate(trace_path, + trace_type_str, + eviction_algo, + cache_size, + eviction_params, + consider_obj_metadata, + ctypes.byref(n_req), + ctypes.byref(n_miss))) + return n_req.value, n_miss.value + +# ----------------------------------------------------------------------------- +# Reader API +# ----------------------------------------------------------------------------- +class Reader: + def __init__(self, + trace_file: Union[str, Path], + reader_params: Dict[str, Any] = None, + ): + """Initialize the Reader. + + Parameters + ---------- + trace_file : str or pathlib.Path + Path to the trace file. + reader_params : dict + Other parameters for the reader. + reference : Reader + Reference reader. + """ + self._handle = ctypes.c_void_p() + # TODO(haocheng): implement reader_params and utilize reference + _safe_call(_LIB.LCS_ReaderCreateFromFile(trace_file, b"",ctypes.byref(self._handle))) + + def __del__(self) -> None: + try: + self._free_handle() + except AttributeError: + pass + + def _free_handle(self) -> "Reader": + if self._handle is not None: + _safe_call(_LIB.LCS_ReaderFree(self._handle)) + self._handle = None + return self + + def read(self) -> "Request": + """Read a request from the reader. + """ + req_handle = ctypes.c_void_p() + _safe_call(_LIB.LCS_ReaderGetNextRequest(self._handle, ctypes.byref(req_handle))) + if not req_handle: + raise StopIteration + return Request(req_handle) + + def get_req_num(self) -> int: + req_num = ctypes.c_int64(0) + _safe_call(_LIB.LCS_ReaderGetReqNum(self._handle, ctypes.byref(req_num))) + return req_num.value + + def get_obj_num(self) -> int: + obj_num = ctypes.c_int64(0) + _safe_call(_LIB.LCS_ReaderGetObjNum(self._handle, ctypes.byref(obj_num))) + return obj_num.value + + def __iter__(self): + while True: + try: + yield self.read() + except StopIteration: + break + + def __next__(self): + return self.read() + + +class Request: + def __init__(self, handle: _ReqHandle): + self._handle = handle + + # def __del__(self) -> None: + # try: + # self._free_handle() + # except AttributeError: + # pass + + # def _free_handle(self) -> "Request": + # if self._handle is not None: + # _safe_call(_LIB.LCS_RequestFree(self._handle)) + # self._handle = None + # return self + +# ----------------------------------------------------------------------------- +# Cache API +# ----------------------------------------------------------------------------- + +class Cache: + def __init__(self, reader: "Reader", + cache_size: float=0.1, + ignore_obj_size: bool=True, + eviction_params: Optional[str]=None): + self.reader = reader + self.cache_size = cache_size + self.ignore_obj_size = ignore_obj_size + self.eviction_params = eviction_params + self._handle = None + + def _free_handle(self) -> "Cache": + if self._handle is not None: + _safe_call(_LIB.LCS_CacheFree(self._handle)) + self._handle = None + return self + + +class FIFO(Cache): + def __init__(self, reader: "Reader", + cache_size: float=0.1, + ignore_obj_size: bool=True, + eviction_params: Optional[str]=b""): + super().__init__(reader, cache_size, ignore_obj_size, eviction_params) + # TODO(haocheng): add more parameters and reference + self._handle = ctypes.c_void_p() + _safe_call(_LIB.LCS_CacheCreate(reader._handle, b"fifo", ctypes.c_float(cache_size), ignore_obj_size, eviction_params, ctypes.byref(self._handle))) + + def get(self, request: "Request") -> bool: + hit = ctypes.c_bool() + _safe_call(_LIB.LCS_CacheGet(self._handle, request._handle, ctypes.byref(hit))) + return hit.value + + def __del__(self) -> None: + try: + self._free_handle() + except AttributeError: + pass + \ No newline at end of file diff --git a/python-package/libcachesim/callback.py b/python-package/libcachesim/callback.py new file mode 100644 index 00000000..331e3a87 --- /dev/null +++ b/python-package/libcachesim/callback.py @@ -0,0 +1,7 @@ +class CacheSizeInvalidException(Exception): + """Exception raised when the cache size is invalid.""" + pass + + + + \ No newline at end of file diff --git a/python-package/libcachesim/libpath.py b/python-package/libcachesim/libpath.py new file mode 100644 index 00000000..f7b322c1 --- /dev/null +++ b/python-package/libcachesim/libpath.py @@ -0,0 +1,51 @@ +# coding: utf-8 +"""Find the path to libCacheSim dynamic library files.""" + +import ctypes +from os import environ +from pathlib import Path +from platform import system +from typing import List + +__all__: List[str] = [] + + +def _find_lib_path() -> List[str]: + """Find the path to libCacheSim library files. + + Returns + ------- + lib_path: list of str + List of all found library paths to libCacheSim. + """ + curr_path = Path(__file__).absolute() + + try: + ctypes.cdll.LoadLibrary("libglib-2.0.so") + except OSError: + raise Exception("Cannot find GLib library. Please install glib2 development package") + + dll_path = [ + curr_path.parents[2] / "_build", + curr_path.parents[2] / "_build" / "libCacheSim" / "python", + ] + if system() in ("Windows", "Microsoft"): + raise Exception("python package does not support Windows") + elif system() == "Darwin": + # TODO: add MacOS support + raise Exception("python package does not support MacOS") + else: + # Linux + dll_path = [p / "libpython.so" for p in dll_path] + lib_path = [str(p) for p in dll_path if p.is_file()] + if not lib_path: + dll_path_joined = "\n".join(map(str, dll_path)) + raise Exception(f"Cannot find libcachesim library file in following paths:\n{dll_path_joined}") + return lib_path + + +_LIB = ctypes.cdll.LoadLibrary(_find_lib_path()[0]) + +# check if the library is loaded successfully +if not _LIB: + raise Exception("Failed to load libcachesim library") diff --git a/python-package/pyproject.toml b/python-package/pyproject.toml new file mode 100644 index 00000000..ac232444 --- /dev/null +++ b/python-package/pyproject.toml @@ -0,0 +1,164 @@ +[project] + +classifiers = [ + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Cache Simulation" +] +dependencies = [ + "numpy>=1.17.0", + "pandas>=1.3.0" +] +description = "libCacheSim Python-package" +license = {file = "LICENSE"} +maintainers = [ + {name = "Haocheng Xia", email = "xhc_1007@163.com"} +] +name = "libCacheSim" +readme = "README.md" +requires-python = ">=3.7" +version = "0.1.0" + +[project.optional-dependencies] + + +[project.urls] + + +# start:build-system +[build-system] + +requires = ["scikit-build-core>=0.10.1"] +build-backend = "scikit_build_core.build" + +# based on https://github.com/scikit-build/scikit-build-core#configuration +[tool.scikit-build] + +cmake.version = "CMakeLists.txt" +ninja.version = ">=1.11" +ninja.make-fallback = true +cmake.args = [ + "-D__BUILD_FOR_PYTHON:BOOL=ON" +] +build.verbose = false +cmake.build-type = "Release" +build.targets = ["_lightgbm"] +# stripping binaries should be turned back on once this is fixed: +# https://github.com/jameslamb/pydistcheck/issues/235 +install.strip = false +logging.level = "INFO" +sdist.reproducible = true +wheel.py-api = "py3" +experimental = false +strict-config = false +minimum-version = "build-system.requires" + +# end:build-system + +[tool.mypy] +disallow_untyped_defs = true +exclude = 'build/*|compile/*|docs/*|examples/*|external_libs/*|lightgbm-python/*|tests/*' +ignore_missing_imports = true + +[tool.ruff] +exclude = [ + "build", + "compile", + "external_libs", + "libCacheSim-python", +] +line-length = 120 + +# this should be set to the oldest version of python LightGBM supports +target-version = "py37" + +[tool.ruff.format] +docstring-code-format = false +exclude = [ + "build/*.py", + "compile/*.py", + "external_libs/*.py", + "libCacheSim-python/*.py", +] +indent-style = "space" +quote-style = "double" +skip-magic-trailing-comma = false + +[tool.ruff.lint] +ignore = [ + # (pydocstyle) Missing docstring in magic method + "D105", + # (pycodestyle) Line too long + "E501", + # (pylint) Too many branches + "PLR0912", + # (pylint) Too many arguments in function definition + "PLR0913", + # (pylint) Too many statements + "PLR0915", + # (pylint) Consider merging multiple comparisons + "PLR1714", + # (pylint) Magic value used in comparison + "PLR2004", + # (pylint) for loop variable overwritten by assignment target + "PLW2901", + # (pylint) use 'elif' instead of 'else' then 'if', to reduce indentation + "PLR5501" +] +select = [ + # flake8-bugbear + "B", + # flake8-comprehensions + "C4", + # pydocstyle + "D", + # pycodestyle (errors) + "E", + # pyflakes + "F", + # isort + "I", + # NumPy-specific rules + "NPY", + # pylint + "PL", + # flake8-return: unnecessary assignment before return + "RET504", + # flake8-simplify: use dict.get() instead of an if-else block + "SIM401", + # flake8-print + "T", + # pycodestyle (warnings) + "W", +] + +[tool.ruff.lint.per-file-ignores] +"docs/conf.py" = [ + # (flake8-bugbear) raise exceptions with "raise ... from err" + "B904", + # (flake8-print) flake8-print + "T" +] +"examples/*" = [ + # pydocstyle + "D", + # flake8-print + "T" +] +"python-package/lightgbm/basic.py" = [ + # (pylint) Using the global statement is discouraged + "PLW0603" +] +"tests/*" = [ + # (flake8-bugbear) Found useless expression + "B018", + # pydocstyle + "D", + # flake8-print + "T" +] + +[tool.ruff.lint.pydocstyle] +convention = "numpy" + +[tool.ruff.lint.isort] +known-first-party = ["libCacheSim"] \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a266a04e..e9a961ea 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,3 @@ - set(coreLib ${ALL_MODULES} ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) add_executable(testReader test_traceReader.c) @@ -20,7 +19,12 @@ add_executable(testPrefetchAlgo test_prefetchAlgo.c) target_link_libraries(testPrefetchAlgo ${coreLib}) add_executable(testDataStructure test_dataStructure.c) -target_link_libraries(testDataStructure ${coreLib}) +if (BUILD_SHARED_LIBS) +# TODO: due to cyclic dependency, we cannot use coreLib here + target_link_libraries(testDataStructure ${PROJECT_NAME}) +else() + target_link_libraries(testDataStructure ${coreLib}) +endif (BUILD_SHARED_LIBS) add_executable(testUtils test_utils.c) target_link_libraries(testUtils ${coreLib})