diff --git a/CMakeLists.txt b/CMakeLists.txt index 936c206c..946a9aea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,6 +375,7 @@ if(LIBPRESSIO_HAS_CUSZ) set(LIBPRESSIO_COMPRESSORS "${LIBPRESSIO_COMPRESSORS} cusz") find_package(CUSZ REQUIRED) libpressio_plugin_file(/compressors/cusz.cc) + libpressio_plugin_file(/compressors/eip.cc) target_link_libraries(libpressio PRIVATE CUSZ::cusz CUDA::cuda_driver CUDA::cudart) endif() @@ -565,6 +566,13 @@ if(LIBPRESSIO_HAS_CUSZP) libpressio_plugin_file(/compressors/cuszp.cc) target_link_libraries(libpressio PRIVATE cuSZp::cuSZp_shared) endif() +option(LIBPRESSIO_HAS_LSCOMP "build the LSCOMP plugin" OFF) +if(LIBPRESSIO_HAS_LSCOMP) + set(LIBPRESSIO_COMPRESSORS "${LIBPRESSIO_COMPRESSORS} LSCOMP") + find_package(cuLSZ REQUIRED) + libpressio_plugin_file(/compressors/lscomp.cc) + target_link_libraries(libpressio PRIVATE cuLSZ::cuLSZ) +endif() option(LIBPRESSIO_HAS_FPZIP "build the fpzip plugin" OFF) if(LIBPRESSIO_HAS_FPZIP) diff --git a/README.md b/README.md index d3bc1f55..73806902 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # LibPressio -Pressio is latin for compression. LibPressio is a C++ library with C compatible bindings to abstract between different lossless and lossy compressors and their configurations. It solves the problem of having to having to write separate application level code for each lossy compressor that is developed. Instead, users write application level code using LibPressio, and the library will make the correct underlying calls to the compressors. It provides interfaces to represent data, compressors settings, and compressors. +Pressio is Latin for compression. LibPressio is a C++ library with C compatible bindings to abstract between different lossless and lossy compressors and their configurations. It solves the problem of having to having to write separate application level code for each lossy compressor that is developed. Instead, users write application level code using LibPressio, and the library will make the correct underlying calls to the compressors. It provides interfaces to represent data, compressors settings, and compressors. Documentation for the `master` branch can be [found here](https://robertu94.github.io/libpressio/) @@ -16,12 +16,23 @@ pressio -i ~/git/datasets/hurricane/100x500x500/CLOUDf48.bin.f32 \ -w /path/to/output.dec ``` -The reccomended way to learn LibPressio is with self-paced [LibPressio Tutorial](https://github.com/robertu94/libpressio_tutorial). +Using the CLI from [`pressio-tools`](https://github.com/robertu94/pressio-tools) you can use +the `pressio_new` command to generate scaffolding code for new projects. + +```bash +#list available templates +pressio_new + +#generate a python sample code using SZ3 +pressio_new client:python sz3 ~/path/to/data +``` + +The recommended way to learn LibPressio is with self-paced [LibPressio Tutorial](https://github.com/robertu94/libpressio_tutorial). Here you will find examples of how to use LibPressio in a series of lessons for several common languages. You can also find a [recording of the tutorial on YouTube](https://youtu.be/hZ_dFCMxmGw). -## Getting Started +## Overview of the library After skimming the example, LibPressio has 6 major headers that you will need to use: @@ -34,39 +45,43 @@ Type | Use `pressio_metrics.h` | A set of metrics to run while compressors run `pressio_io.h` | An extension header that provides methods to load or store data from/to persistent storage -All of these are included by the convience header `libpressio.h`. + +All of these are included by the convenience header `libpressio.h`. + +There is also a C++ interface that can be bound in `libpressio_ext/cpp/libpressio.h` which provides access to unstable features +and the ability to extend the library. You can pick up the more advanced features as you need them. -You can also find more examples in `test/` or in the [LibPressio intresting scripts collection](https://github.com/robertu94/libpressio-interesting-scripts) which catalogs intresting higher-level use cases. +You can also find more examples in `test/` or in the [LibPressio interesting scripts collection](https://github.com/robertu94/libpressio-interesting-scripts) which catalogs interesting higher-level use cases. ## Supported Compressors and Metrics -Libpressio provides a number of builtin compressor and metrics modules. +LibPressio provides a number of builtin compressor and metrics modules. All of these are **disabled by default**. They can be enabled by passing the corresponding `LIBPRESSIO_HAS_*` variable to CMake. -Additionally, Libpressio is extensible. +Additionally, LibPressio is extensible. For information on writing a compressor plugin see [Writing a Compressor Plugin](docs/WritingACompressorPlugin.md) For information on writing a metrics plugin see [Writing a Metrics Plugin](docs/WritingAMetricsPlugin.md) ### Compressor Plugins -1st party compressors plugins can be found in [src/plugins/compressors](https://github.com/robertu94/libpressio/tree/master/src/plugins/compressors) +First party compressors plugins can be found in [src/plugins/compressors](https://github.com/robertu94/libpressio/tree/master/src/plugins/compressors) See the [compressor settings page](build/Compressors.md) for information on how to configure them. ### Metrics Plugins -1st party compressors plugins can be found in [src/plugins/metrics](https://github.com/robertu94/libpressio/tree/master/src/plugins/metrics) +First party compressors plugins can be found in [src/plugins/metrics](https://github.com/robertu94/libpressio/tree/master/src/plugins/metrics) See the [metrics results page](build/Metrics.md) for information on what they produce ### IO Plugins -1st party compressors plugins can be found in [src/plugins/io](https://github.com/robertu94/libpressio/tree/master/src/plugins/io) +First party compressors plugins can be found in [src/plugins/io](https://github.com/robertu94/libpressio/tree/master/src/plugins/io) See the [io settings page](build/IO.md) for information on how to configure them @@ -74,7 +89,7 @@ See the [io settings page](build/IO.md) for information on how to configure them ## Installing LibPressio using Spack -LibPressio can be built using [spack](https://github.com/spack/spack/). This example will install libpressio with only the SZ3 plugin. +LibPressio can be built using [spack](https://github.com/spack/spack/). This example will install LibPressio with only the SZ3 plugin. ```bash git clone https://github.com/spack/spack @@ -82,7 +97,7 @@ source ./spack/share/spack/setup-env.sh spack install libpressio+sz3 ``` -More information on spack can be found in the [spack documentation](https://spack.readthedocs.io/en/latest/) or [my quick start guides for systems that I use](https://robertu94.github.io/guides) +More information on Spack can be found in the [Spack documentation](https://spack.readthedocs.io/en/latest/) or [my quick start guides for systems that I use](https://robertu94.github.io/guides) You can see the other available versions and compilation options by calling `spack info libpressio` @@ -90,10 +105,10 @@ The following language bindings are in this repository. + `C` -- (default) if you need a stable interface + `C++` -- (default) if you want a more productive interface, or want to extend LibPressio -+ `Python` -- (`+python`; BUILD_PYTHON_WRAPPER) if you know or want to intergate Python ++ `Python` -- (`+python`; BUILD_PYTHON_WRAPPER) if you know or want to integrate Python + `HDF5` -- (`+hdf5+json`; LIBPRESSIO_HAS_HDF AND LIBPRESSIO_HAS_JSON) you already use HDF5 -The following bindings must be installed seperately: +The following bindings must be installed separately: + `R` -- [r-libpressio](https://github.com/robertu94/libpressio-r) if you know or want to integrate with R + `Bash/CLI` -- [libpressio-tools](https://github.com/robertu94/pressio-tools) if you want to quickly prototype from the CLI @@ -103,16 +118,19 @@ The following bindings are experimental and can be installed manually: + `Julia` -- [libpressio-jl](https://github.com/robertu94/LibPressio.jl) if you know or want to integrate with Julia + `Rust` -- [libpressio-rs](https://github.com/robertu94/libpressio-rs) if you know or want to integrate with Rust -## Doing a development build with spack +## Doing a development build with Spack -The easiest way to do a development build of libpressio is to use Spack envionments. +The easiest way to do a development build of LibPressio is to use Spack environments. ```bash -# one time setup: create an envionment +# one time setup: install the Spack repo for pre-release versions +spack repo add https://github.com/robertu94/spack_packages + +# one time setup: create an environment spack env create -d mydevenviroment spack env activate mydevenvionment -# one time setup: tell spack to set LD_LIBRARY_PATH with the spack envionment's library paths +# one time setup: tell Spack to set LD_LIBRARY_PATH with the Spack environment's library paths spack config add modules:prefix_inspections:lib64:[LD_LIBRARY_PATH] spack config add modules:prefix_inspections:lib:[LD_LIBRARY_PATH] @@ -137,16 +155,16 @@ Libpressio unconditionally requires: + `gcc-4.8.5` or later + `clang-7.0.0` or later using either `libc++` or `libstdc++`. Beware that system libraries may need to be recompiled with `libc++` if using `libc++` -Dependency versions and optional dependencies are documented [in the spack package](https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/libpressio/package.py). +Dependency versions and optional dependencies are documented [in the Spack package](https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/libpressio/package.py). ## Configuring LibPressio Manually -LibPressio uses a fairly standard CMake buildsystem. +LibPressio uses a fairly standard CMake build-system. For more information on [CMake refer to these docs](https://robertu94.github.io/learning/cmake) The set of configuration options for LibPressio can be found using `cmake -L $BUILD_DIR`. -For information on what these settings do, see the [spack package](https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/libpressio/package.py) +For information on what these settings do, see the [Spack package](https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/libpressio/package.py) # API Stability @@ -158,13 +176,13 @@ Please refer to [CONTRIBUTORS.md](CONTRIBUTORS.md) for a list of contributors, s # Bug Reports -Please files bugs to the Github Issues page on the CODARCode libpressio repository. +Please files bugs to the GitHub Issues page on the CODARCode libpressio repository. Please read this post on [how to file a good bug report](https://codingnest.com/how-to-file-a-good-bug-report/).  After reading this post, please provide the following information specific to libpressio: + Your OS version and distribution information, usually this can be found in `/etc/os-release` + the output of `cmake -L $BUILD_DIR` -+ the version of each of libpressio's dependencies listed in the README that you have installed. Where possible, please provide the commit hashes. ++ the version of each of LibPressio's dependencies listed in the README that you have installed. Where possible, please provide the commit hashes. # Citing LibPressio diff --git a/include/libpressio_ext/cpp/configurable.h b/include/libpressio_ext/cpp/configurable.h index fbbdb64d..8b0945c4 100644 --- a/include/libpressio_ext/cpp/configurable.h +++ b/include/libpressio_ext/cpp/configurable.h @@ -11,10 +11,10 @@ * \brief interface for configurable types */ +namespace libpressio { /** * Base interface for configurable objects in libpressio */ -namespace libpressio { class pressio_configurable : public pressio_errorable { public: virtual ~pressio_configurable()=default; diff --git a/include/libpressio_ext/cpp/data.h b/include/libpressio_ext/cpp/data.h index e1e9ba06..057e5e8d 100644 --- a/include/libpressio_ext/cpp/data.h +++ b/include/libpressio_ext/cpp/data.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "pressio_data.h" #include "memory.h" @@ -18,6 +19,37 @@ namespace libpressio { + namespace detail { + template + size_t tuple_bytes(std::tuple const&) { + return (0 + ... + sizeof(T)); + } + + template + std::array>::value> tuple_sizes(std::tuple const&){ + std::array>::value> s {sizeof(T)...}; + return s; + }; + template + std::array>::value> sizes(){ + std::array>::value> s {sizeof(T)...}; + return s; + }; + + + template + void tuple_for_each_with_index_impl(Tuple&& t, F&& f, std::index_sequence) { + (f(std::get(t), I),...); + } + + template + void tuple_for_each_with_index(Tuple&& t, F&& f) { + return tuple_for_each_with_index_impl(std::forward(t), std::forward(f), + std::make_index_sequence< + std::tuple_size>::value>{}); + } + } + /** * \file * \brief C++ pressio_data interface @@ -57,11 +89,50 @@ size_t data_size_in_bytes(pressio_dtype type, size_t const dimensions, size_t co } +enum pressio_data_header { + pressio_data_header_none = 0, /*no header, all type info must be stored separately*/ + pressio_data_header_len = 1, /*just the length of each buffer must be stored*/ + pressio_data_header_lentype = 2, /*just the length and type of each buffer must be stored*/ + pressio_data_header_dimstype = 3, /*just the dimension and type of each buffer must be stored*/ +}; + /** * represents a data buffer that may or may not be owned by the class */ struct pressio_data { + /** + * allocates a new buffer with the contents of all of the bufs concatenated together + * \param[in] bufs the data to concatonate + * \param[in] header include a header to aid in restoring + * \param[in] domain which domain to create the buffer in + */ + static pressio_data join(std::vector&& bufs, pressio_data_header header, std::shared_ptr&& domain); + static pressio_data join(std::vector&& bufs, pressio_data_header header); + static pressio_data join(std::vector&& bufs); + + /** + * Splits the buffer created with ::join + * \param[in] input the buffer to be split + * \param[in] has_header if the data has a header or not + * \param[out] bufs the split buffers + */ + static void split(pressio_data input, pressio_data_header header, std::vector& bufs); + /** + * Splits the buffer created with ::join + * \param[in] input the buffer to be split + * \param[out] bufs the split buffers + */ + static void split(pressio_data input, std::vector& bufs); + /** + * allocates a new empty data buffer with a given type in a domain + * + * \param[in] dtype the type the buffer will contain + * \param[in] domain where the data will be allocated if it were provided + * \returns an empty data object (i.e. has no data) + * \see pressio_data_new_empty + * */ + static pressio_data type_domain(const pressio_dtype dtype, std::shared_ptr&& domain); /** * allocates a new empty data buffer * @@ -531,6 +602,35 @@ struct pressio_data { } } + template + pressio_data(std::tupleconst& t): pressio_data(pressio_data::owning(pressio_byte_dtype, {::libpressio::detail::tuple_bytes(t)})) + { + auto sizes = ::libpressio::detail::sizes(); + auto offsets = sizes; + std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), 0); + auto copy = [this, &offsets, &sizes](auto&& v, size_t i) { + memcpy(static_cast(data()) + offsets[i], &v, sizes[i]); + }; + ::libpressio::detail::tuple_for_each_with_index(t, copy); + + } + /** + * convert a packed binary pressio_data structure into std::tuple + * \returns the tuple containing the data + */ + template + T to_tuple() const { + T t; + auto sizes = ::libpressio::detail::tuple_sizes(t); + auto offsets = sizes; + std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), 0); + auto copy = [this, &offsets, &sizes](auto&& v, size_t i) { + memcpy(&v, static_cast(data()) + offsets[i], sizes[i]); + }; + ::libpressio::detail::tuple_for_each_with_index(t, copy); + + return t; + } /** * convert a pressio_data structure into a 1d c++ standard vector. If the type doesn't match, it will be casted first * \returns the vector containing the data diff --git a/include/libpressio_ext/cpp/domain_send.h b/include/libpressio_ext/cpp/domain_send.h index 6bc71b15..62d6fae9 100644 --- a/include/libpressio_ext/cpp/domain_send.h +++ b/include/libpressio_ext/cpp/domain_send.h @@ -8,13 +8,30 @@ struct pressio_data; namespace libpressio { namespace domains { + /** + * a plugin that understands how to transfer data from one memory domain (e.g. malloc, cudamalloc) to another + */ struct pressio_domain_send { + /** + * create an instance of a send plugin + */ pressio_domain_send()=default; + /** + * destroy an instance of a send plugin + */ virtual ~pressio_domain_send()=default; + /** + * copy data from src to dst. Throws an exception on error + * \param[in] src the source data buffer + * \param[in] dst the destination data buffer + */ virtual void send(pressio_data& dst, pressio_data const& src) const = 0; }; } +/** + * registry for all send plugins + */ pressio_registry>& domain_send_plugins(); } diff --git a/include/libpressio_ext/cpp/memory.h b/include/libpressio_ext/cpp/memory.h index 954468f3..8134b97b 100644 --- a/include/libpressio_ext/cpp/memory.h +++ b/include/libpressio_ext/cpp/memory.h @@ -9,6 +9,10 @@ * \brief C++ interface to managed memory objects from domaines */ +/** + * an interface to manage linear memories on various domains + * This just tracks the pointer and the capacity, higher level functions can be found in pressio_data + */ class pressio_memory { private: std::shared_ptr data_domain; diff --git a/src/basic_indexer.h b/src/basic_indexer.h index 6f4fb913..e7f14afe 100644 --- a/src/basic_indexer.h +++ b/src/basic_indexer.h @@ -24,12 +24,12 @@ struct basic_indexer { }(args)) {} template - basic_indexer(It first, It second) noexcept: - max_dims([](It first, It second){ + basic_indexer(It first, It ) noexcept: + max_dims([](It first){ std::array dims; - std::copy(first, second, dims.begin()); + std::copy_n(first, N, dims.begin()); return dims; - }(first, second)) { + }(first)) { } template diff --git a/src/plugins/compressors/cusz.cc b/src/plugins/compressors/cusz.cc index 508d556a..41592361 100644 --- a/src/plugins/compressors/cusz.cc +++ b/src/plugins/compressors/cusz.cc @@ -9,7 +9,7 @@ #include "libpressio_ext/cpp/domain_manager.h" #include #include -#include +#include #include #include #include @@ -232,27 +232,6 @@ class cusz_compressor_plugin : public libpressio_compressor_plugin { return set_error(12, cudaGetErrorString(ec)); } - int cusz_error(pszerror ec) { - switch(ec) { - case CUSZ_FAIL_ONDISK_FILE_ERROR: - return set_error(3, "ondisk file error"); - case CUSZ_FAIL_DATA_NOT_READY: - return set_error(4, "data not ready"); - case CUSZ_FAIL_GPU_MALLOC: - return set_error(5, "gpu malloc"); - case CUSZ_FAIL_GPU_MEMCPY: - return set_error(6, "gpu memcpy"); - case CUSZ_FAIL_GPU_ILLEGAL_ACCESS: - return set_error(7, "gpu illegal access"); - case CUSZ_FAIL_GPU_OUT_OF_MEMORY: - return set_error(8, "gpu out of memory"); - case CUSZ_FAIL_INCOMPRESSIABLE: - return set_error(9, "incompressible"); - default: - return set_error(10, "unknown error"); - } - } - #define lp_check_cuda_error(call) { \ cudaError ec = (call); \ if(ec != cudaSuccess) \ @@ -316,15 +295,15 @@ class cusz_compressor_plugin : public libpressio_compressor_plugin { T* d_uncomp = (T*)input.data(); psz_header header; - psz::TimeRecord timerecord; psz_len3 uncomp_len = psz_len3{dims[0], dims[1], dims[2]}; psz_compressor* comp = psz_create(to_cuszdtype(input.dtype()), uncomp_len, to_cusz_predictor_type(predictor), radius, Huffman); // codectype Huffman is hardcoded. (v0.10rc) uint8_t* ptr_compressed; size_t compressed_len; + void* ignored = nullptr; psz_compress( comp, d_uncomp, uncomp_len, err_bnd, to_cuszmode(eb_mode), - &ptr_compressed, &compressed_len, &header, &timerecord, *stream.get()); + &ptr_compressed, &compressed_len, &header, ignored, *stream.get()); *output = pressio_data::move(pressio_byte_dtype, ptr_compressed, {compressed_len}, domain_plugins().build("cudamalloc")); *output = domain_manager().make_readable(domain_plugins().build("malloc"), std::move(*output)); @@ -345,14 +324,15 @@ class cusz_compressor_plugin : public libpressio_compressor_plugin { pressio_data cpu_input = domain_manager().make_readable(domain_plugins().build("malloc"), *real_input); psz_header* header = (psz_header*)cpu_input.data(); auto comp_len = pszheader_filesize(header); + psz_len3 decomp_len = psz_len3{dims[0], dims[1], dims[2]}; // x, y, z // pressio_data gpu_input = domain_manager().make_readable(domain_plugins().build("cudamalloc"), *real_input); uint8_t* ptr_compressed = (uint8_t*)gpu_input.data(); - psz::TimeRecord timerecord; + void* ignored = nullptr; psz_compressor* comp = psz_create_from_header(header); - psz_decompress(comp, ptr_compressed, comp_len, d_decomp, decomp_len, (void*)&timerecord, *stream.get()); + psz_decompress(comp, ptr_compressed, comp_len, d_decomp, decomp_len, ignored, *stream.get()); psz_release(comp); return 0; diff --git a/src/plugins/compressors/eip.cc b/src/plugins/compressors/eip.cc new file mode 100644 index 00000000..e0f37ca8 --- /dev/null +++ b/src/plugins/compressors/eip.cc @@ -0,0 +1,234 @@ + +#include +#include +#include "std_compat/memory.h" +#include "libpressio_ext/cpp/compressor.h" +#include "libpressio_ext/cpp/data.h" +#include "libpressio_ext/cpp/options.h" +#include "libpressio_ext/cpp/pressio.h" +#include "libpressio_ext/cpp/domain_manager.h" +#include "cusz/config/eip.hh" +#include "cusz/kernel/predictor.hh" +#include "cusz/kernel/spv.hh" +#include "cusz/mem/cxx_backends.h" +#include "cusz/hf_hl.hh" +#include "cusz/mem/compbuf_pbk.hh" + +namespace libpressio { namespace compressors { namespace eip_ns { + + + constexpr size_t Radius = 128; +using T = float; using M = uint32_t; +using E = uint16_t; +template using PC_ref = psz::PredConfig>; +template using GPU_xlrz_ref = psz::module::GPU_x_lorenzo_nd>; +using Buf = psz::CompressorBufferPbk2; +using PC_eip = psz::PredConfig< + T, psz::PredFunc< + Toggle::ZigZagDisabled, Toggle::StatLocalEnabled, Toggle::StatGlobalDisabled, + Toggle::QuantGroupingDisabled, Toggle::FutureEIPEnabled>>; +using GPU_EIP_comp_r128 = psz::module::GPU_c_lorenzo_1d_eip; +using BufToggle = psz::CompressorBufferPbkToggle; + +BufToggle toggle_eip{.pbk_all = true}; + +class eip_compressor_plugin : public libpressio_compressor_plugin { + +private: + std::unique_ptr eip; + std::array dims; + double bound = 1e-4; +public: + eip_compressor_plugin(): libpressio_compressor_plugin(), eip(std::make_unique(1, 1, 1, false /* will revise in the future*/, &toggle_eip)), dims{1,1,1} { + } + eip_compressor_plugin(eip_compressor_plugin const& rhs): libpressio_compressor_plugin(rhs), eip(std::make_unique(rhs.dims[0], rhs.dims[1], rhs.dims[2], false /* will revise in the future*/, &toggle_eip)), dims(rhs.dims) { + // Buffer size need to be deteremined on intialization. + eip = std::make_unique(1, 1, 1, false /* will revise in the future*/, &toggle_eip); + } + eip_compressor_plugin& operator=(eip_compressor_plugin const& rhs){ + // Buffer size need to be deteremined on intialization. + dims = rhs.dims; + eip = std::make_unique(dims[0], dims[1], dims[2], false /* will revise in the future*/, &toggle_eip); + return *this; + } + + + struct pressio_options get_options_impl() const override + { + struct pressio_options options; + set(options, "pressio:abs", bound); + return options; + } + + struct pressio_options get_configuration_impl() const override + { + struct pressio_options options; + set(options, "pressio:thread_safe", pressio_thread_safety_multiple); + set(options, "pressio:stability", "experimental"); + std::vector invalidations {}; + std::vector invalidation_children {}; + set(options, "predictors:error_dependent", get_accumulate_configuration("predictors:error_dependent", invalidation_children, invalidations)); + set(options, "predictors:error_agnostic", get_accumulate_configuration("predictors:error_agnostic", invalidation_children, invalidations)); + set(options, "predictors:runtime", get_accumulate_configuration("predictors:runtime", invalidation_children, invalidations)); + set(options, "pressio:highlevel", get_accumulate_configuration("pressio:highlevel", invalidation_children, std::vector{})); + return options; + } + + struct pressio_options get_documentation_impl() const override + { + struct pressio_options options; + set(options, "pressio:description", R"()"); + return options; + } + + + int set_options_impl(struct pressio_options const& options) override + { + get(options, "pressio:abs", &bound); + return 0; + } + + + int compress_impl(const pressio_data* real_input, + struct pressio_data* output) override + { + if(real_input->dtype() != pressio_float_dtype) return set_error(1, "unsupported type"); + auto input = domain_manager().make_readable(domain_plugins().build("cudamalloc"), *real_input); + + + cudaStream_t stream; + cudaStreamCreate(&stream); + + GPU_EIP_comp_r128::kernel( + (float*)input.data(), input.num_elements(), (void*)eip->outlier(), bound, eip->pbk_books(), + eip->pbk_book_IDs(), eip->pbk_bitstream(), eip->pbk_bits(), + eip->pbk_entries(), eip->pbk_loc(), eip->pbk_break(), stream); + + auto endloc = eip->pbk_encoding_endloc(); // contains a d2h copy of one size_t + auto brlen = eip->host_get_pbk_break_num(); + auto splen = eip->host_get_unpredictable_num(); + + // right now, the errno is arbirarily defined. + std::stringstream ss; + if (brlen >= eip->pbk_break_num_max() - 1) { + ss << "[psz::warning::pbk_run::EIP] max allowed enc-breaking (" + << eip->pbk_break_num_max() << ") exceeded"; + return set_error(1, ss.str()); + } + if (splen >= eip->unpredictable_num_max() - 1) { + ss << "[psz::warning::pbk_run::EIP] max allowed unpredictable (" + << eip->outlier_ratio() << " * input-len) exceeded"; + return set_error(1, ss.str()); + } + + *output = pressio_data::join({ + pressio_data(std::make_tuple(endloc, brlen, splen)), + pressio_data::nonowning(pressio_dtype_from_typepbk_book_IDs())>>(), eip->pbk_book_IDs(), {eip->num_chunk()}, "cudamalloc"), + pressio_data::nonowning(pressio_dtype_from_typepbk_entries())>>(), eip->pbk_entries(), {eip->num_chunk()}, "cudamalloc"), + pressio_data::nonowning(pressio_dtype_from_typepbk_bits())>>(), eip->pbk_bits(), {eip->num_chunk()}, "cudamalloc"), + pressio_data::nonowning(pressio_dtype_from_typepbk_bitstream())>>(), eip->pbk_bitstream(), {endloc}, "cudamalloc"), + pressio_data::nonowning(pressio_dtype_from_typepbk_break_val())>>(), eip->pbk_break_val(), {brlen}, "cudamalloc"), + pressio_data::nonowning(pressio_dtype_from_typepbk_break_idx())>>(), eip->pbk_break_idx(), {brlen}, "cudamalloc"), + pressio_data::nonowning(pressio_dtype_from_typeunpredictable_val())>>(), eip->unpredictable_val(), {splen}, "cudamalloc"), + pressio_data::nonowning(pressio_dtype_from_typeunpredictable_idx())>>(), eip->unpredictable_idx(), {splen}, "cudamalloc"), + }); + + + return 0; + } + + int decompress_impl(const pressio_data* real_input, + struct pressio_data* output) override + { + auto input = domain_manager().make_readable(domain_plugins().build("cudamalloc"), *real_input); + std::vector restore{ + pressio_data(), + pressio_data::type_domain(pressio_dtype_from_typepbk_book_IDs())>>(), domain_plugins().build("cudamalloc")), + pressio_data::type_domain(pressio_dtype_from_typepbk_entries())>>(), domain_plugins().build( "cudamalloc")), + pressio_data::type_domain(pressio_dtype_from_typepbk_bits())>>(), domain_plugins().build( "cudamalloc")), + pressio_data::type_domain(pressio_dtype_from_typepbk_bitstream())>>(), domain_plugins().build( "cudamalloc")), + pressio_data::type_domain(pressio_dtype_from_typepbk_break_val())>>(), domain_plugins().build( "cudamalloc")), + pressio_data::type_domain(pressio_dtype_from_typepbk_break_idx())>>(), domain_plugins().build( "cudamalloc")), + pressio_data::type_domain(pressio_dtype_from_typeunpredictable_val())>>(), domain_plugins().build( "cudamalloc")), + pressio_data::type_domain(pressio_dtype_from_typeunpredictable_idx())>>(), domain_plugins().build( "cudamalloc")), + }; + pressio_data::split(input, restore); + auto metadata = std::move(restore.at(0)); + auto book_ids = std::move(restore.at(1)); + auto entries = std::move(restore.at(2)); + auto bits = std::move(restore.at(3)); + auto bitstream = std::move(restore.at(4)); + auto break_val = std::move(restore.at(5)); + auto break_idx = std::move(restore.at(6)); + auto unpred_val = std::move(restore.at(7)); + auto unpred_idx = std::move(restore.at(8)); + *output = domain_manager().make_writeable(domain_plugins().build("cudamalloc"), std::move(*output)); + + // The following variables are from the compression archive: + // len (the original #elements), splen, brlen) + + // Also, assume mem_eip buffer is reused. + + size_t splen, brlen, endloc; + std::tie(endloc, brlen, splen) = restore.at(0).to_tuple>(); + + + cudaStream_t stream; + cudaStreamCreate(&stream); + if (splen) + psz::spv_scatter_naive( + (T*)unpred_val.data(), (uint32_t*)unpred_idx.data(), splen, (float*)output->data(), + nullptr, stream); + cudaStreamSynchronize(stream); + + size_t len = output->num_elements(); + pressio_data ectrl_eip(pressio_data::owning(pressio_dtype_from_type(), {len})); + phf::cuhip::modules::CPU_pbk_coarse_decode( + (uint32_t*)bitstream.data(), endloc, eip->pbk_revbooks_h(), eip->PBK_RVBK_BYTES(), + (uint8_t*)book_ids.data(), (uint16_t*)bits.data(), (uint32_t*)entries.data(), + (E*)ectrl_eip.data(), len); + + // request an h2d copy: use host buffer for Huffman decoding + ectrl_eip = domain_manager().make_readable(domain_plugins().build("cudamalloc"), std::move(ectrl_eip)); + + // Then fix the breakings during parallel encoding + if (brlen) + psz::spv_scatter_naive( + (uint16_t*)break_val.data(), (uint32_t*)break_idx.data(), brlen, (uint16_t*)ectrl_eip.data(), nullptr, + stream); + cudaStreamSynchronize(stream); + + auto d_space = (T*)output->data(), d_xdata = (T*)output->data(); // aliases + + std::array len3_std; + GPU_xlrz_ref::kernel(eip->ectrl(), d_space, d_xdata, len3_std, bound, Radius, stream); + cudaStreamSynchronize(stream); + + + + return 0; + } + + int major_version() const override { return 0; } + int minor_version() const override { return 0; } + int patch_version() const override { return 1; } + const char* version() const override { return "0.0.1"; } + const char* prefix() const override { return "eip"; } + + pressio_options get_metrics_results_impl() const override { + return {}; + } + + std::shared_ptr clone() override + { + return compat::make_unique(*this); + } + +}; + +pressio_register registration(compressor_plugins(), "eip", []() { + return compat::make_unique(); +}); + +} } } + diff --git a/src/plugins/compressors/lscomp.cc b/src/plugins/compressors/lscomp.cc new file mode 100644 index 00000000..6774e628 --- /dev/null +++ b/src/plugins/compressors/lscomp.cc @@ -0,0 +1,149 @@ + +#include "cuda_runtime.h" +#include "std_compat/memory.h" +#include "libpressio_ext/cpp/compressor.h" +#include "libpressio_ext/cpp/data.h" +#include "libpressio_ext/cpp/options.h" +#include "libpressio_ext/cpp/pressio.h" +#include "libpressio_ext/cpp/domain_manager.h" +#include +#include +#include + + + +namespace libpressio { namespace compressors { namespace lscomp_ns { + +class lscomp_compressor_plugin : public libpressio_compressor_plugin { +public: + struct pressio_options get_options_impl() const override + { + struct pressio_options options; + set(options, "lscomp:pooling_threshold", poolingTH); + set(options, "lscomp:quantization_bins", quantBinsData); + return options; + } + + struct pressio_options get_configuration_impl() const override + { + struct pressio_options options; + set(options, "pressio:thread_safe", pressio_thread_safety_multiple); + set(options, "pressio:stability", "experimental"); + std::vector invalidations {}; + std::vector invalidation_children {}; + set(options, "predictors:error_dependent", get_accumulate_configuration("predictors:error_dependent", invalidation_children, invalidations)); + set(options, "predictors:error_agnostic", get_accumulate_configuration("predictors:error_agnostic", invalidation_children, invalidations)); + set(options, "predictors:runtime", get_accumulate_configuration("predictors:runtime", invalidation_children, invalidations)); + set(options, "pressio:highlevel", get_accumulate_configuration("pressio:highlevel", invalidation_children, std::vector{})); + return options; + } + + struct pressio_options get_documentation_impl() const override + { + struct pressio_options options; + set(options, "pressio:description", R"(A specialized compressors for lightsources focusing on CSSI beamlines + described in detail in "lsCOMP: Efficient Light Source Compression" published at SC25)"); + set(options, "lscomp:pooling_threshold", "the pooling threhsold"); + set(options, "lscomp:quantization_bins", "the quantization bins for the 4 top levels, x<=y<=z<=w"); + return options; + } + + + int set_options_impl(struct pressio_options const& options) override + { + get(options, "lscomp:pooling_threshold", &poolingTH); + pressio_data tmp; + if(get(options, "lscomp:quantization_bins", &tmp) == pressio_options_key_set) { + tmp = tmp.cast(pressio_uint32_dtype); + std::vector v = tmp.to_vector(); + if(v.size() != 4) return set_error(1, "size of quantization bins must be 4"); + if(!std::is_sorted(v.begin(), v.end())) return set_error(1, "quantization bins values must be increasing"); + quantBinsData = tmp; + } + return 0; + } + + int compress_impl(const pressio_data* real_input, + struct pressio_data* output) override + { + if(real_input->dtype() != pressio_uint32_dtype || real_input->dtype() != pressio_uint16_dtype) return set_error(1, "unsupported datatype"); + auto input = domain_manager().make_readable(domain_plugins().build("cudamalloc"), *real_input); + auto dims_sizet = input.normalized_dims(3,1); + size_t cmpSize = 0; + uint3 dims {(uint)dims_sizet[0], (uint)dims_sizet[1], (uint)dims_sizet[2]}; + std::vector quantBinsV = quantBinsData.to_vector(); + uint4 quantBins {quantBinsV[0], quantBinsV[1], quantBinsV[2], quantBinsV[3]}; + cudaStream_t stream; + cudaStreamCreate(&stream); + + pressio_data comp_bytes(pressio_data::owning(input.dtype(), {input.num_elements()})); + switch(input.dtype()) { + case pressio_uint32_dtype: + cuLSZ_compression_uint32_bsize64((uint32_t*)input.data(), (uint8_t*)comp_bytes.data(), &cmpSize, dims, quantBins, poolingTH, stream); + break; + case pressio_uint16_dtype: + cuLSZ_compression_uint16_bsize64((uint16_t*)input.data(), (uint8_t*)comp_bytes.data(), &cmpSize, dims, quantBins, poolingTH, stream); + break; + default: + cudaStreamDestroy(stream); + return set_error(1, "unsupported datatype"); + } + *output = std::move(comp_bytes); + output->reshape({cmpSize}); + output->set_dtype(pressio_byte_dtype); + cudaStreamDestroy(stream); + *output = std::move(input); + return 0; + } + + int decompress_impl(const pressio_data* real_input, + struct pressio_data* real_output) override + { + auto input = domain_manager().make_readable(domain_plugins().build("cudamalloc"), std::move(*real_input)); + auto output = domain_manager().make_writeable(domain_plugins().build("cudamalloc"), std::move(*real_output)); + auto dims_sizet = output.normalized_dims(3,1); + uint3 dims {(uint)dims_sizet[0], (uint)dims_sizet[1], (uint)dims_sizet[2]}; + std::vector quantBinsV = quantBinsData.to_vector(); + uint4 quantBins {quantBinsV[0], quantBinsV[1], quantBinsV[2], quantBinsV[3]}; + cudaStream_t stream; + cudaStreamCreate(&stream); + switch(output.dtype()) { + case pressio_uint32_dtype: + cuLSZ_decompression_uint32_bsize64((uint32_t*)output.data(), (uint8_t*)input.data(), input.size_in_bytes(), dims, quantBins, poolingTH, stream); + break; + case pressio_uint16_dtype: + cuLSZ_decompression_uint16_bsize64((uint16_t*)output.data(), (uint8_t*)input.data(), input.size_in_bytes(), dims, quantBins, poolingTH, stream); + break; + default: + cudaStreamDestroy(stream); + return set_error(1, "unsupported datatype"); + } + cudaStreamDestroy(stream); + return 0; + } + + int major_version() const override { return 0; } + int minor_version() const override { return 0; } + int patch_version() const override { return 1; } + const char* version() const override { return "0.0.1"; } + const char* prefix() const override { return "lscomp"; } + + pressio_options get_metrics_results_impl() const override { + return {}; + } + + std::shared_ptr clone() override + { + return compat::make_unique(*this); + } + + float poolingTH=0.5f; + pressio_data quantBinsData{3u, 5u, 10u, 15u}; +}; + +pressio_register registration(compressor_plugins(), "lscomp", []() { + return compat::make_unique(); +}); + +} } } + diff --git a/src/plugins/compressors/sz3.cc b/src/plugins/compressors/sz3.cc index 23cab9c5..cb469bd8 100644 --- a/src/plugins/compressors/sz3.cc +++ b/src/plugins/compressors/sz3.cc @@ -120,8 +120,8 @@ class sz3_compressor_plugin : public libpressio::compressors::libpressio_compres set(options, "sz3:intrep_algo_str", keys(sz3_options().interp_algo)); set(options, "sz3:algorithm_str", keys(sz3_options().algo)); - std::vector invalidations {"sz3:abs_error_bound", "sz3:rel_error_bound", "sz3:psnr_error_bound", "sz3:l2_norm_error_bound", "sz3:error_bound_mode", "sz3:algorithm", "sz3:lorenzo", "sz3:lorenzo2", "sz3:regression", "sz3:regression2", "sz3:openmp", "sz3:lossless", "sz3:encoder", "sz3:interp_algo", "sz3:interp_direction", "sz3:interp_block_size", "sz3:quant_bin_size", "sz3:stride", "sz3:pred_dim", "pressio:abs", "pressio:rel", "sz3:error_bound_mode_str", "sz3:intrep_algo_str", "sz3:algorithm_str"}; - std::vector runtime_invalidations {"sz3:abs_error_bound", "sz3:rel_error_bound", "sz3:psnr_error_bound", "sz3:l2_norm_error_bound", "sz3:error_bound_mode", "sz3:algorithm", "sz3:lorenzo", "sz3:lorenzo2", "sz3:regression", "sz3:regression2", "sz3:openmp", "sz3:lossless", "sz3:encoder", "sz3:interp_algo", "sz3:interp_direction", "sz3:interp_block_size", "sz3:quant_bin_size", "sz3:stride", "sz3:pred_dim", "pressio:abs", "pressio:rel", "pressio:nthreads", "sz3:error_bound_mode_str", "sz3:intrep_algo_str", "sz3:algorithm_str"}; + std::vector invalidations {"sz3:abs_error_bound", "sz3:rel_error_bound", "sz3:psnr_error_bound", "sz3:l2_norm_error_bound", "sz3:error_bound_mode", "sz3:algorithm", "sz3:lorenzo", "sz3:lorenzo2", "sz3:regression", "sz3:regression2", "sz3:openmp", "sz3:interp_algo", "sz3:interp_direction", "sz3:quant_bin_size", "sz3:pred_dim", "pressio:abs", "pressio:rel", "sz3:error_bound_mode_str", "sz3:intrep_algo_str", "sz3:algorithm_str"}; + std::vector runtime_invalidations {"sz3:abs_error_bound", "sz3:rel_error_bound", "sz3:psnr_error_bound", "sz3:l2_norm_error_bound", "sz3:error_bound_mode", "sz3:algorithm", "sz3:lorenzo", "sz3:lorenzo2", "sz3:regression", "sz3:regression2", "sz3:openmp", "sz3:interp_algo", "sz3:interp_direction", "sz3:quant_bin_size", "sz3:pred_dim", "pressio:abs", "pressio:rel", "pressio:nthreads", "sz3:error_bound_mode_str", "sz3:intrep_algo_str", "sz3:algorithm_str"}; std::vector invalidation_children {}; set(options, "predictors:error_dependent", get_accumulate_configuration("predictors:error_dependent", invalidation_children, invalidations)); @@ -149,13 +149,9 @@ class sz3_compressor_plugin : public libpressio::compressors::libpressio_compres set(options, "sz3:regression", "use the regression predictor"); set(options, "sz3:regression2", "use the 2nd order regression predictor"); set(options, "sz3:openmp", "use openmp parallelization"); - set(options, "sz3:lossless", "lossless compression method to apply; 1 bypass lossless, 1 zstd"); - set(options, "sz3:encoder", "which encoder to use, 0 skip encoder, 1 huffman, 2 arithmatic"); set(options, "sz3:interp_algo", "which intrepolation algorithm to use"); set(options, "sz3:interp_direction", "which interpolation direction to use"); - set(options, "sz3:interp_block_size", "what block size to use for interpolation to use"); set(options, "sz3:quant_bin_size", "number of quantization bins"); - set(options, "sz3:stride", "stride between items"); set(options, "sz3:pred_dim", "prediction dimension"); set(options, "sz3:algorithm_str", "compression algorithm"); set(options, "sz3:error_bound_mode_str", "error bound"); diff --git a/src/plugins/compressors/szp.cc b/src/plugins/compressors/szp.cc index 3b6d3091..87c4824b 100644 --- a/src/plugins/compressors/szp.cc +++ b/src/plugins/compressors/szp.cc @@ -133,8 +133,12 @@ class szp_compressor_plugin : public libpressio::compressors::libpressio_compres } auto input = domain_manager().make_readable(domain_plugins().build("malloc"), *real_input); size_t outSize = 0; - unsigned char *bytes = szp_compress(fastMode, to_dtype(input.dtype()), input.data(), &outSize, errBoundMode, absBound, relBound, input.num_elements(), block_size); - *output = pressio_data::move(pressio_byte_dtype, bytes, {outSize}, domain_plugins().build("malloc")); + try { + unsigned char *bytes = szp_compress(fastMode, to_dtype(input.dtype()), input.data(), &outSize, errBoundMode, absBound, relBound, input.num_elements(), block_size); + *output = pressio_data::move(pressio_byte_dtype, bytes, {outSize}, domain_plugins().build("malloc")); + } catch (std::runtime_error const& ex) { + return set_error(1, ex.what()); + } return 0; } @@ -148,8 +152,12 @@ class szp_compressor_plugin : public libpressio::compressors::libpressio_compres num_threads = [old_threads]{omp_set_num_threads(old_threads);}; } auto input = domain_manager().make_readable(domain_plugins().build("malloc"), *real_input); - void* data = (void*)szp_decompress(fastMode, to_dtype(output->dtype()), reinterpret_cast(input.data()), input.num_elements(), output->num_elements(), block_size); - *output = pressio_data::move(output->dtype(), data, output->dimensions(), domain_plugins().build("malloc")); + try { + void* data = (void*)szp_decompress(fastMode, to_dtype(output->dtype()), reinterpret_cast(input.data()), input.num_elements(), output->num_elements(), block_size); + *output = pressio_data::move(output->dtype(), data, output->dimensions(), domain_plugins().build("malloc")); + } catch(std::runtime_error const& ex) { + return set_error(1, ex.what()); + } return 0; } diff --git a/src/plugins/metrics/out_of_bounds.cc b/src/plugins/metrics/out_of_bounds.cc index 90856b64..8a197b01 100644 --- a/src/plugins/metrics/out_of_bounds.cc +++ b/src/plugins/metrics/out_of_bounds.cc @@ -111,23 +111,38 @@ class out_of_bounds_plugin : public libpressio_metrics_plugin { } struct pressio_options get_configuration_impl() const override { - pressio_options opts; - set(opts, "pressio:stability", "experimental"); - set(opts, "pressio:thread_safe", pressio_thread_safety_multiple); - set(opts, "predictors:requires_decompress", true); - set(opts, "predictors:invalidate", std::vector{"predictors:error_dependent"}); - return opts; + pressio_options opts; + set(opts, "pressio:stability", "experimental"); + set(opts, "pressio:thread_safe", pressio_thread_safety_multiple); + set(opts, "predictors:requires_decompress", true); + set(opts, "predictors:invalidate", std::vector{"predictors:error_dependent"}); + return opts; } struct pressio_options get_documentation_impl() const override { - pressio_options opt; - set(opt, "pressio:description", R"(produces a report of the values that are out of bounds)"); - set(opt, "out_of_bounds:oob", "a 3xN array where [i*3]= input, [i*3+1]=output, [i*3+2]=error"); - set(opt, "out_of_bounds:index", "a length N array containing the indexes of the out of range values"); - set(opt, "out_of_bounds:print_first_k", - "print the first k entries that are out of range in a human-focused unstable format for " - "debugging"); - return opt; + pressio_options opt; + set(opt, "pressio:description", R"(produces a report of the values that are out of bounds)"); + set(opt, "out_of_bounds:oob", "a 3xN array where [i*3]= input, [i*3+1]=output, [i*3+2]=error"); + set(opt, "out_of_bounds:index", "a length N array containing the indexes of the out of range values"); + set(opt, "out_of_bounds:print_first_k", + "print the first k entries that are out of range in a human-focused unstable format for " + "debugging"); + set(opt, "pressio:abs", R"(a pointwise absolute error bound + + compressors may provide this value without supporting abs=0. + compressors that support abs=0, additionally should also define pressio:lossless + )"); + set(opt, "pressio:rel", R"(a pointwise value-range relative error bound + + compressors may provide this value without supporting rel=0. + compressors that support rel=0, additionally should also define pressio:lossless + )"); + set(opt, "pressio:pw_rel", R"(a pointwise relative error bound + + compressors may provide this value without supporting pw_rel=0. + compressors that support pw_rel=0, additionally should also define pressio:lossless + )"); + return opt; } pressio_options get_metrics_results(pressio_options const &) override { diff --git a/src/pressio_data.cc b/src/pressio_data.cc index ddac6d76..0e6397b6 100644 --- a/src/pressio_data.cc +++ b/src/pressio_data.cc @@ -8,6 +8,7 @@ #include "libpressio_ext/cpp/data.h" #include "./plugins/domains/user.h" #include "libpressio_ext/cpp/domain.h" +#include "libpressio_ext/cpp/domain_manager.h" #if LIBPRESSIO_HAS_CUDA #include @@ -175,6 +176,228 @@ namespace { }; } +pressio_data pressio_data::join(std::vector&& bufs, pressio_data_header header, std::shared_ptr&& domain) { + std::vector sizes (bufs.size()); + std::transform(bufs.begin(), bufs.end(), sizes.begin(), [](pressio_data const& d){return d.size_in_bytes(); }); + + size_t header_size = 0; + switch(header) { + case pressio_data_header_dimstype: + header_size = bufs.size() * (sizeof(uint64_t) + sizeof(uint8_t)); + for(auto const& buf: bufs) { + header_size += (sizeof(uint64_t) * buf.num_dimensions()); + } + break; + case pressio_data_header_lentype: + header_size = bufs.size() * (sizeof(uint64_t) + sizeof(uint8_t)); + break; + case pressio_data_header_len: + header_size = bufs.size() * sizeof(uint64_t); + break; + case pressio_data_header_none: + break; + } + + std::vector offsets (sizes.size()); + std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), header_size); + size_t total_size = offsets.back() + sizes.back(); + pressio_data out = pressio_data::owning(pressio_byte_dtype, {total_size}, domain->clone()); + + pressio_data header_buf; + switch(header) { + case pressio_data_header_dimstype: + { + header_buf = pressio_data::owning(pressio_byte_dtype, {header_size}); + uint64_t* end_dims = std::transform(bufs.begin(), bufs.end(), (uint64_t*)header_buf.data(), + [](pressio_data const& buf) {return (uint8_t)buf.num_dimensions();}); + uint8_t* end_types = std::transform(bufs.begin(), bufs.end(), (uint8_t*)end_dims, + [](pressio_data const& buf) {return (uint8_t)buf.dtype();}); + for(auto const& buf: bufs) { + end_types = (uint8_t*)std::copy(buf.dims.begin(), buf.dims.end(), (uint64_t*)end_types); + } + } + break; + case pressio_data_header_lentype: + header_buf = pressio_data::owning(pressio_byte_dtype, {header_size}); + std::copy(sizes.begin(), sizes.end(), (uint64_t*)header_buf.data()); + std::transform(bufs.begin(), bufs.end(), (uint8_t*)header_buf.data()+(sizeof(uint64_t)*bufs.size()), + [](pressio_data const& buf) {return (uint8_t)buf.dtype();}); + break; + case pressio_data_header_len: + header_buf = pressio_data(sizes.begin(), sizes.end()); + break; + case pressio_data_header_none: + break; + } + if(header != pressio_data_header_none) { + auto dom_buf = domain_manager().copy_to(domain->clone(), header_buf); + domain->memcpy(static_cast(out.data()),dom_buf.data(),header_size); + } + for(size_t i = 0; i < offsets.size(); ++i) { + auto dom_buf = domain_manager().make_readable(domain->clone(), bufs[i]); + domain->memcpy(static_cast(out.data())+offsets[i],dom_buf.data(),sizes[i]); + } + return out; + +} +pressio_data pressio_data::join(std::vector&& bufs, pressio_data_header header) { + if(bufs.empty()) throw std::runtime_error("inferring domain for pressio_data::join requires at least one buffer"); + return pressio_data::join(std::move(bufs), header, bufs.front().domain()->clone()); +} +pressio_data pressio_data::join(std::vector&& bufs) { + return pressio_data::join(std::move(bufs), pressio_data_header_dimstype); +} + +void pressio_data::split(pressio_data input, pressio_data_header header, std::vector& bufs) { + switch(header) { + case pressio_data_header_dimstype: + { + //first copy enough to determine the full header size + const size_t inital_header = bufs.size()* (sizeof(uint64_t)+sizeof(uint8_t)); + auto header = pressio_data::nonowning(pressio_uint8_dtype, input.data(), {inital_header}, input.domain()->domain_id()); + auto cpu_header = domain_manager().make_readable(libpressio::domain_plugins().build("malloc"), header); + auto header_ptr = static_cast(cpu_header.data()); + std::vector n_dims(header_ptr, header_ptr+bufs.size()); + size_t header_size = bufs.size()*(sizeof(uint64_t)+sizeof(uint8_t)); + for(size_t i = 0; i < bufs.size(); ++i) { + header_size += (sizeof(uint64_t)* n_dims[i]); + } + + //then copy the entire header + header = pressio_data::nonowning(pressio_uint8_dtype, input.data(), {header_size}, input.domain()->domain_id()); + cpu_header = domain_manager().make_readable(libpressio::domain_plugins().build("malloc"), header); + + std::vector types(bufs.size()); + std::vector> dims(bufs.size()); + std::transform( + (uint8_t*)header_ptr+(bufs.size() * sizeof(uint64_t)), + (uint8_t*)header_ptr+(bufs.size() * (sizeof(uint64_t)+ sizeof(uint8_t))), + types.data(), + [](uint8_t i) { return static_cast(i); } + ); + uint64_t* ndims_ptr = (uint64_t*)((uint8_t*)(header_ptr) + (bufs.size() * (sizeof(uint64_t)+sizeof(uint8_t)))); + + + for(size_t i = 0; i < bufs.size(); ++i) { + dims[i].resize(n_dims[i]); + std::copy( + ndims_ptr, + ndims_ptr+n_dims[i], + dims[i].begin()); + ndims_ptr += n_dims[i]; + } + std::vector offsets(bufs.size()); + std::vector sizes(bufs.size()); + for(size_t i = 0; i < bufs.size(); ++i) { + sizes[i] = libpressio::data_size_in_bytes(types[i], dims[i].size(), dims[i].data()); + } + std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), header_size); + for(size_t i = 0; i< bufs.size(); ++i) { + if(bufs[i].has_data()) { + if(bufs[i].size_in_bytes() != sizes[i]) throw std::runtime_error("size of dst and src do not match"); + auto in_dom = domain_manager().make_readable(bufs[i].domain(), pressio_data::nonowning(bufs[i].dtype(), static_cast(input.data()) + offsets[i], bufs[i].dimensions(), input.domain()->domain_id())); + bufs[i].domain()->memcpy(bufs[i].data(), in_dom.data(), sizes[i]); + } else { + //determine buf size from type and sizes + size_t n = sizes[i]/pressio_dtype_size(types[i]); + if(sizes[i]%pressio_dtype_size(types[i]) != 0) throw std::runtime_error("invalid dtype for size"); + //allocate buf + bufs[i] = pressio_data::owning(types[i], {n}, bufs[i].domain()); + + //copy + auto in_dom = domain_manager().make_readable(bufs[i].domain(), pressio_data::nonowning(types[i], static_cast(input.data()) + offsets[i], bufs[i].dimensions(), input.domain()->domain_id())); + bufs[i].domain()->memcpy(bufs[i].data(), in_dom.data(), sizes[i]); + } + } + } + break; + case pressio_data_header_lentype: + { + const size_t header_size = bufs.size()*(sizeof(uint64_t)+sizeof(uint8_t)); + auto header = pressio_data::nonowning(pressio_uint8_dtype, input.data(), {bufs.size()}, input.domain()->domain_id()); + auto cpu_header = domain_manager().make_readable(libpressio::domain_plugins().build("malloc"), header); + auto header_ptr = static_cast(cpu_header.data()); + std::vector sizes(header_ptr, header_ptr+bufs.size()); + std::vector types(bufs.size()); + std::transform( + (uint8_t*)header_ptr+(bufs.size() * sizeof(uint64_t)), + (uint8_t*)header_ptr+(bufs.size() * (sizeof(uint64_t)+ sizeof(uint8_t))), + types.data(), + [](uint8_t i) { return static_cast(i); } + ); + std::vector offsets(bufs.size()); + std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), header_size); + for(size_t i = 0; i< bufs.size(); ++i) { + if(bufs[i].has_data()) { + if(bufs[i].size_in_bytes() != sizes[i]) throw std::runtime_error("size of dst and src do not match"); + auto in_dom = domain_manager().make_readable(bufs[i].domain(), pressio_data::nonowning(bufs[i].dtype(), static_cast(input.data()) + offsets[i], bufs[i].dimensions(), input.domain()->domain_id())); + bufs[i].domain()->memcpy(bufs[i].data(), in_dom.data(), sizes[i]); + } else { + //determine buf size from type and sizes + size_t n = sizes[i]/pressio_dtype_size(types[i]); + if(sizes[i]%pressio_dtype_size(types[i]) != 0) throw std::runtime_error("invalid dtype for size"); + //allocate buf + bufs[i] = pressio_data::owning(types[i], {n}, bufs[i].domain()); + + //copy + auto in_dom = domain_manager().make_readable(bufs[i].domain(), pressio_data::nonowning(types[i], static_cast(input.data()) + offsets[i], bufs[i].dimensions(), input.domain()->domain_id())); + bufs[i].domain()->memcpy(bufs[i].data(), in_dom.data(), sizes[i]); + } + } + } + break; + case pressio_data_header_len: { + auto header = pressio_data::nonowning(pressio_uint64_dtype, input.data(), {bufs.size()}, input.domain()->domain_id()); + auto cpu_header = domain_manager().make_readable(libpressio::domain_plugins().build("malloc"), header); + auto header_ptr = static_cast(cpu_header.data()); + const size_t header_size = bufs.size()*sizeof(uint64_t); + std::vector sizes(header_ptr, header_ptr+bufs.size()); + std::vector offsets(bufs.size()); + std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), header_size); + for(size_t i = 0; i< bufs.size(); ++i) { + if(bufs[i].has_data()) { + if(bufs[i].size_in_bytes() != sizes[i]) throw std::runtime_error("size of dst and src do not match"); + auto in_dom = domain_manager().make_readable(bufs[i].domain(), pressio_data::nonowning(bufs[i].dtype(), static_cast(input.data()) + offsets[i], bufs[i].dimensions(), input.domain()->domain_id())); + bufs[i].domain()->memcpy(bufs[i].data(), in_dom.data(), sizes[i]); + } else { + //determine buf size from type and sizes + size_t n = sizes[i]/pressio_dtype_size(bufs[i].dtype()); + if(sizes[i]%pressio_dtype_size(bufs[i].dtype()) != 0) throw std::runtime_error("invalid dtype for size"); + //allocate buf + bufs[i] = pressio_data::owning(bufs[i].dtype(), {n}, bufs[i].domain()); + + //copy + auto in_dom = domain_manager().make_readable(bufs[i].domain(), pressio_data::nonowning(bufs[i].dtype(), static_cast(input.data()) + offsets[i], bufs[i].dimensions(), input.domain()->domain_id())); + bufs[i].domain()->memcpy(bufs[i].data(), in_dom.data(), sizes[i]); + } + } + } + break; + case pressio_data_header_none: + { + //sizes come from bufs + std::vector sizes(bufs.size()); + std::vector offsets(bufs.size()); + std::transform(bufs.begin(), bufs.end(), sizes.begin(), [](pressio_data& d) { return d.size_in_bytes(); }); + std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), 0); + for(size_t i = 0; i< bufs.size(); ++i) { + if(!bufs[i].has_data()) { + bufs[i] = pressio_data::owning(bufs[i].dtype(), bufs[i].dimensions(), bufs[i].domain()); + } + auto in_dom = domain_manager().make_readable(bufs[i].domain(), pressio_data::nonowning(bufs[i].dtype(), static_cast(input.data()) + offsets[i], bufs[i].dimensions(), input.domain()->domain_id())); + bufs[i].domain()->memcpy(bufs[i].data(), in_dom.data(), sizes[i]); + } + } + break; + } +} +void pressio_data::split(pressio_data input, std::vector& bufs) { + return pressio_data::split(input, pressio_data_header_dimstype, bufs); +} + +pressio_data pressio_data::type_domain(const pressio_dtype dtype, std::shared_ptr && domain) { + return pressio_data(dtype, {}, pressio_memory(std::move(domain))); + } pressio_data pressio_data::empty(const pressio_dtype dtype, std::vector const& dimensions, std::shared_ptr && domain) { return pressio_data(dtype, dimensions, pressio_memory(std::move(domain))); } diff --git a/test/test_compressor_integration.cc b/test/test_compressor_integration.cc index 9ee55311..65b8024e 100644 --- a/test/test_compressor_integration.cc +++ b/test/test_compressor_integration.cc @@ -9,6 +9,9 @@ using namespace libpressio; std::set> skip_list { {"SZauto", "3d float zeros"}, {"qoz", "1d float"}, + {"szp", "1d int"}, + {"szp", "2d int"}, + {"szp", "3d int"}, {"qoz", "1d int"}, {"qoz", "3d int"}, {"qoz", "3d float zeros"}, @@ -19,6 +22,7 @@ std::set> skip_list { {"cusz", "2d float"}, {"cusz", "2d 0-1 float"}, {"cusz", "3d float zeros"}, + {"tthresh", "3d float zeros"}, }; template diff --git a/test/test_pressio_data.cc b/test/test_pressio_data.cc index bfadc426..440b1bef 100644 --- a/test/test_pressio_data.cc +++ b/test/test_pressio_data.cc @@ -316,3 +316,39 @@ TEST(test_mulit_dimensional_array, test_mulit_dimensional_array_operator) { pressio_data_free(data); } + +TEST(test_to_from_tuple, test_to_from_tuple) { + std::tuple t{ + 3, 4.2f, 3.7, 'x' + }; + auto tuple_data = pressio_data(t); + EXPECT_EQ(tuple_data.size_in_bytes(), 4+4+8+1); + auto result = tuple_data.to_tuple(); + EXPECT_EQ(t, result); +} +TEST(test_join_split, test_join_split_headerlen) { + pressio_data a{1,2,3}; + pressio_data b{1.2,2.3,3.4}; + pressio_data c{1.2f,2.3f}; + pressio_data joined = pressio_data::join({a,b,c}, pressio_data_header_len); + std::vector s{ + pressio_data::empty(pressio_int32_dtype, {}), + pressio_data::empty(pressio_double_dtype, {}), + pressio_data::empty(pressio_float_dtype, {}), + }; + pressio_data::split(joined, pressio_data_header_len, s); + EXPECT_EQ(s[0], a); + EXPECT_EQ(s[1], b); + EXPECT_EQ(s[2], c); +} +TEST(test_join_split, test_join_split_dimstype) { + pressio_data a{1,2,3}; + pressio_data b{1.2,2.3,3.4}; + pressio_data c{1.2f,2.3f}; + pressio_data joined = pressio_data::join({a,b,c}, pressio_data_header_dimstype); + std::vector s(3); + pressio_data::split(joined, pressio_data_header_dimstype, s); + EXPECT_EQ(s[0], a); + EXPECT_EQ(s[1], b); + EXPECT_EQ(s[2], c); +}