From a785e0f93f72e599faf5766643cf4769bdc15141 Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 9 Jul 2023 01:02:28 -0700 Subject: [PATCH] Added a custom chunk manager for cached chunk slicing. --- include/tatami/chunked/CustomChunkManager.hpp | 325 ++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/src/chunked/CustomChunkManager.cpp | 3 + 3 files changed, 329 insertions(+) create mode 100644 include/tatami/chunked/CustomChunkManager.hpp create mode 100644 tests/src/chunked/CustomChunkManager.cpp diff --git a/include/tatami/chunked/CustomChunkManager.hpp b/include/tatami/chunked/CustomChunkManager.hpp new file mode 100644 index 00000000..fd91587c --- /dev/null +++ b/include/tatami/chunked/CustomChunkManager.hpp @@ -0,0 +1,325 @@ +#ifndef TATAMI_CUSTOM_CHUNK_MANAGER_HPP +#define TATAMI_CUSTOM_CHUNK_MANAGER_HPP + +#include +#include + +namespace tatami { + +template +class CustomChunkManager { +public: + std::vector chunks; + size_t chunk_nrow; + size_t chunk_ncol; + size_t num_chunks_per_row; + size_t num_chunks_per_column; + bool row_major; + +private: + template + size_t get_primary_chunkdim() const { + if constexpr(accrow_) { + return chunk_nrow; + } else { + return chunk_ncol; + } + } + + template + size_t get_secondary_chunkdim() const { + if constexpr(accrow_) { + return chunk_ncol; + } else { + return chunk_nrow; + } + } + + template + size_t get_primary_num_chunks() const { + if constexpr(accrow_) { + return num_chunks_per_row; + } else { + return num_chunks_per_column; + } + } + +public: + static constexpr bool sparse_chunk = Chunk_::sparse; + + struct SparseCache { + SparseCache(size_t primary_dim) : cache_indices(primary_dim), cache_values(primary_dim) {} + + std::vector > cache_indices; + std::vector > cache_values; + + std::vector buffer_indptrs; + std::vector buffer_indices; + std::vector buffer_values; + }; + + struct DenseCache { + DenseCache(size_t length, size_t primary_dim) : cache(length * primary_dim) {} + + std::vector cache; + + std::vector buffer; + }; + + typedef typename std::conditional::type Cache; + + template + Cache create_chunk_cache(size_t length) const { + if constexpr(sparse_chunk) { + return SparseCache(exact_ ? 1 : get_primary_chunkdim()); + } else { + return DenseCache(exact_ ? 1 : length, get_primary_chunkdim()); + } + } + +public: + template + void extract_block(size_t i, size_t start, size_t length, Cache& cache) { + if (!length) { + return; + } + + size_t primary_num_chunks = get_primary_num_chunks(); + size_t primary_chunkdim = get_primary_chunkdim(); + size_t secondary_chunkdim = get_secondary_chunkdim(); + + size_t primary_chunk_index = (i / primary_chunkdim); + size_t shift = (row_major ? num_chunks_per_row : num_chunks_per_column); + size_t increment = (row_major == accrow_ ? 1 : shift); + size_t offset = primary_chunk_index * shift; + + size_t start_chunk_index = start / secondary_chunkdim; + size_t end_chunk_index = (start + length + secondary_chunkdim - 1) / secondary_chunkdim; // i.e., ceiling of the integer division. + + size_t primary_start_pos = 0, primary_end_pos = primary_chunkdim; + if constexpr(exact_) { + primary_start_pos = i - primary_chunk_index * primary_chunkdim; + primary_end_pos = primary_start_pos + 1; + } + + size_t dense_cache_offset = 0; + size_t secondary_start_pos = start_chunk_index * secondary_chunkdim; + + for (size_t c = start_chunk_index; c < end_chunk_index; ++c) { + const auto& chunk = chunks[offset]; + size_t from = (c == start_chunk_index ? start - secondary_start_pos : 0); + size_t to = (c + 1 == end_chunk_index ? start + length - secondary_start_pos : secondary_chunkdim); + + if constexpr(sparse_chunk) { + chunk.inflate(cache.buffer_values, cache.buffer_indices, cache.buffer_indptrs); + typename Chunk_::index_type secondary_offset = secondary_start_pos; + + if (chunk.row_major == accrow_) { + for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { + auto start = cache.buffer_indptrs[p], end = cache.buffer_indptrs[p + 1]; + if (start == end) { + continue; + } + + if (from) { + start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, from) - cache.buffer_indices.begin(); + } + if (to != secondary_chunkdim) { + end = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, to) - cache.buffer_indices.begin(); + } + + auto pout = (exact_ ? 0 : p); + cache.cache_values[pout].insert(cache.cache_values[p].end(), cache.buffer_values.begin() + start, cache.buffer_values.begin() + end); + for (size_t i = start; i < end; ++i) { + cache.cache_indices[pout].push_back(cache.buffer_indices[i] + secondary_offset); + } + } + + } else { + for (size_t s = from; s < to; ++s) { + auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; + if constexpr(exact_) { + start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, primary_start_pos) - cache.buffer_indices.begin(); + if (start != end && cache.buffer_indices[start] == primary_start_pos) { + cache.cache_values[0].push_back(cache.buffer_values[start]); + cache.cache_indices[0].push_back(s + secondary_offset); + } + + } else { + for (size_t i = start; i < end; ++i) { + auto p = cache.buffer_indices[i]; + cache.cache_values[p].push_back(cache.buffer_values[i]); + cache.cache_indices[p].push_back(s + secondary_offset); + } + } + } + } + + } else { + chunk.inflate(cache.buffer); + auto bptr = cache.buffer.data(); + auto cptr = cache.cache.data() + dense_cache_offset; + + if (chunk.row_major == accrow_) { + for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { + std::copy(bptr + from, bptr + to, cptr); + bptr += secondary_chunkdim; + cptr += length; + } + + } else { + for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { + auto copy_bptr = bptr; + auto copy_cptr = cptr; + for (size_t s = from; s < to; ++s) { + *copy_cptr = *copy_bptr; + ++copy_cptr; + copy_bptr += primary_chunkdim; + } + ++bptr; + cptr += length; + } + } + + dense_cache_offset += (to - from); + } + + offset += increment; + secondary_start_pos += secondary_chunkdim; + } + } + +public: + template + void extract_dense_block(size_t i, const std::vector& indices, Cache& cache) { + if (indices.empty()) { + return; + } + + size_t primary_num_chunks = get_primary_num_chunks(); + size_t primary_chunkdim = get_primary_chunkdim(); + size_t secondary_chunkdim = get_secondary_chunkdim(); + + size_t primary_chunk_index = (i / primary_chunkdim); + size_t shift = (row_major ? num_chunks_per_row : num_chunks_per_column); + size_t increment = (row_major == accrow_ ? 1 : shift); + size_t offset = primary_chunk_index * shift; + + size_t start_chunk_index = indices.front() / secondary_chunkdim; + std::vector collected; + auto iIt = indices.begin(); + + size_t primary_start_pos = 0, primary_end_pos = primary_chunkdim; + if constexpr(exact_) { + primary_start_pos = i - primary_chunk_index * primary_chunkdim; + primary_end_pos = primary_start_pos + 1; + } + + size_t dense_cache_offset = 0; + size_t secondary_start_pos = start_chunk_index * secondary_chunkdim; + + for (size_t c = start_chunk_index; c < primary_num_chunks && iIt != indices.end(); ++c) { + Index_ secondary_end_pos = secondary_start_pos + secondary_chunkdim; + collected.clear(); + while (iIt != indices.end() && *iIt < secondary_end_pos) { + collected.push_back(*iIt - secondary_start_pos); + ++iIt; + } + + if (!collected.empty()) { + const auto& chunk = chunks[offset]; + + if constexpr(sparse_chunk) { + chunk.inflate(cache.buffer_values, cache.buffer_indices, cache.buffer_indptrs); + typename Chunk_::index_type secondary_offset = secondary_start_pos; + + if (chunk.row_major == accrow_) { + for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { + auto start = cache.buffer_indptrs[p], end = cache.buffer_indptrs[p + 1]; + if (start == end) { + continue; + } + + if (collected.front()) { + start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, collected.front()) - cache.buffer_indices.begin(); + } + + auto pout = (exact_ ? 0 : p); + auto cIt = collected.begin(); + for (size_t i = start; i < end; ++i) { + Index_ target = cache.buffer_indices[i]; + while (cIt != collected.end() && *cIt < target) { + ++cIt; + } + if (cIt == collected.end()) { + break; + } + if (*cIt == target) { + cache.cache_values[pout].push_back(cache.buffer_values[i]); + cache.cache_indices[pout].push_back(cache.buffer_indices[i] + secondary_offset); + ++cIt; + } + } + } + + } else { + for (auto s : collected) { + auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; + if constexpr(exact_) { + start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, primary_start_pos) - cache.buffer_indices.begin(); + if (start != end && cache.buffer_indices[start] == primary_start_pos) { + cache.cache_values[0].push_back(cache.buffer_values[start]); + cache.cache_indices[0].push_back(s + secondary_offset); + } + + } else { + for (size_t i = start; i < end; ++i) { + auto p = cache.buffer_indices[i]; + cache.cache_values[p].push_back(cache.buffer_values[i]); + cache.cache_indices[p].push_back(s + secondary_offset); + } + } + } + } + + } else { + chunk.inflate(cache.buffer); + auto bptr = cache.buffer.data(); + auto cptr = cache.cache.data() + dense_cache_offset; + + if (chunk.row_major == accrow_) { + for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { + auto copy_cptr = cptr; + for (auto x : collected) { + *copy_cptr = bptr[x]; + ++copy_cptr; + } + bptr += secondary_chunkdim; + cptr += indices.size(); + } + + } else { + for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { + auto copy_cptr = cptr; + for (auto x : collected) { + *copy_cptr = bptr[x * primary_chunkdim]; + ++copy_cptr; + } + ++bptr; + cptr += indices.size(); + } + } + + dense_cache_offset += collected.size(); + } + } + + offset += increment; + secondary_start_pos += secondary_chunkdim; + } + } +}; + +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0b325ce2..cc3e0fb8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,6 +41,7 @@ add_executable( src/chunked/LruChunkCache.cpp src/chunked/OracleChunkCache.cpp + src/chunked/CustomChunkManager.cpp src/stats/sums.cpp src/stats/variances.cpp diff --git a/tests/src/chunked/CustomChunkManager.cpp b/tests/src/chunked/CustomChunkManager.cpp new file mode 100644 index 00000000..7bc0dcdc --- /dev/null +++ b/tests/src/chunked/CustomChunkManager.cpp @@ -0,0 +1,3 @@ +#include +#include "tatami/chunked/CustomChunkManager.hpp" +