From a06cd7935a8f3537a6fbdc84d592d80668d4e2fc Mon Sep 17 00:00:00 2001 From: amete <serhanmete@gmail.com> Date: Wed, 12 Aug 2020 14:06:27 +0200 Subject: [PATCH 1/5] Switch to using ThinningInfo for the lossy float compression inside copyAuxStoreThinned --- .../AthContainers/tools/copyAuxStoreThinned.h | 12 ++ .../share/copyAuxStoreThinned_test.ref | 48 ++++++++ .../AthContainers/src/copyAuxStoreThinned.cxx | 81 ++++++++------ .../test/copyAuxStoreThinned_test.cxx | 105 +++++++++++++++++- 4 files changed, 212 insertions(+), 34 deletions(-) diff --git a/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h b/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h index faeb2498d2a5..ac10b6af72bf 100644 --- a/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h +++ b/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h @@ -40,6 +40,18 @@ void copyAuxStoreThinned (const SG::IConstAuxStore& orig, SG::IAuxStore& copy, const SG::ThinningInfo* info); +/** + * @brief Helper method to apply lossy float compression + * @param dst Pointer to the start of the vector's data + * @param idst Index of element in vector + * @param eltSize Element size for the auxid + * @param typeName Type name for the auxid + * @param nmantissa Compression level to be used for the auxid + */ +void lossyFloatCompress (void* dst, size_t dst_index, + const std::size_t& eltSize, + const std::string& typeName, + const unsigned int& nmantissa); } // namespace SG diff --git a/Control/AthContainers/share/copyAuxStoreThinned_test.ref b/Control/AthContainers/share/copyAuxStoreThinned_test.ref index a5bce3fd2565..0f41f8db3453 100644 --- a/Control/AthContainers/share/copyAuxStoreThinned_test.ref +++ b/Control/AthContainers/share/copyAuxStoreThinned_test.ref @@ -1 +1,49 @@ test1 + +Testing lossy float compression... + +Iteration [0] + +Original value 0.512345 (binary : 0 01111110 00000110010100100001011) +Compressed value 0.511719 (binary : 0 01111110 00000110000000000000000) +Original value at [0] 0.512345 (binary : 0 01111110 00000110010100100001011) +Compressed value at [0] 0.511719 (binary : 0 01111110 00000110000000000000000) +Original value at [1] 0.123456 (binary : 0 01111011 11111001101011010000000) +Compressed value at [1] 0.123535 (binary : 0 01111011 11111010000000000000000) + +Iteration [1] + +Original value 1.51234 (binary : 0 01111111 10000011001010010000101) +Compressed value 1.51562 (binary : 0 01111111 10000100000000000000000) +Original value at [0] 1.51234 (binary : 0 01111111 10000011001010010000101) +Compressed value at [0] 1.51562 (binary : 0 01111111 10000100000000000000000) +Original value at [1] 2.12346 (binary : 0 10000000 00001111110011010110100) +Compressed value at [1] 2.125 (binary : 0 10000000 00010000000000000000000) + +Iteration [2] + +Original value 2.51235 (binary : 0 10000000 01000001100101001000011) +Compressed value 2.51562 (binary : 0 10000000 01000010000000000000000) +Original value at [0] 2.51235 (binary : 0 10000000 01000001100101001000011) +Compressed value at [0] 2.51562 (binary : 0 10000000 01000010000000000000000) +Original value at [1] 4.12346 (binary : 0 10000001 00000111111001101011010) +Compressed value at [1] 4.125 (binary : 0 10000001 00001000000000000000000) + +Iteration [3] + +Original value 3.51235 (binary : 0 10000000 11000001100101001000011) +Compressed value 3.51562 (binary : 0 10000000 11000010000000000000000) +Original value at [0] 3.51235 (binary : 0 10000000 11000001100101001000011) +Compressed value at [0] 3.51562 (binary : 0 10000000 11000010000000000000000) +Original value at [1] 6.12346 (binary : 0 10000001 10000111111001101011010) +Compressed value at [1] 6.125 (binary : 0 10000001 10001000000000000000000) + +Iteration [4] + +Original value 4.51234 (binary : 0 10000001 00100000110010100100001) +Compressed value 4.5 (binary : 0 10000001 00100000000000000000000) +Original value at [0] 4.51234 (binary : 0 10000001 00100000110010100100001) +Compressed value at [0] 4.5 (binary : 0 10000001 00100000000000000000000) +Original value at [1] 8.12346 (binary : 0 10000010 00000011111100110101101) +Compressed value at [1] 8.125 (binary : 0 10000010 00000100000000000000000) + diff --git a/Control/AthContainers/src/copyAuxStoreThinned.cxx b/Control/AthContainers/src/copyAuxStoreThinned.cxx index 6043683eed10..2bd46dfe8b21 100644 --- a/Control/AthContainers/src/copyAuxStoreThinned.cxx +++ b/Control/AthContainers/src/copyAuxStoreThinned.cxx @@ -18,6 +18,7 @@ #include "AthContainersInterfaces/IAuxStoreCompression.h" #include "AthenaKernel/ThinningDecisionBase.h" #include "CxxUtils/no_sanitize_undefined.h" +#include "CxxUtils/FloatCompressor.h" #include <vector> @@ -48,7 +49,7 @@ void copyAuxStoreThinned NO_SANITIZE_UNDEFINED const ThinningDecisionBase* dec = info ? info->m_decision : nullptr; size_t nremaining = dec ? dec->thinnedSize() : size; - + // Access the auxiliary type registry: SG::AuxTypeRegistry& r = SG::AuxTypeRegistry::instance(); @@ -62,17 +63,8 @@ void copyAuxStoreThinned NO_SANITIZE_UNDEFINED sel_auxids = iio->getSelectedAuxIDs(); } - // Get the auxiliary IDs of the variables that should be compressed - SG::auxid_set_t comp_auxids_high; - SG::auxid_set_t comp_auxids_low; - const IAuxStoreCompression* icomp = dynamic_cast<const IAuxStoreCompression*> (&orig); - if (icomp != nullptr) { - comp_auxids_high = icomp->getCompressedAuxIDs(true); // High compression - comp_auxids_low = icomp->getCompressedAuxIDs(false); // Low compression - } - copy.resize (nremaining); - + // Loop over all the variables of the original container: for (SG::auxid_t auxid : auxids) { // Skip null auxids (happens if we don't have the dictionary) @@ -110,12 +102,11 @@ void copyAuxStoreThinned NO_SANITIZE_UNDEFINED } } - // Get the element size and the type name for the current auxid - const size_t eltSize{r.getEltSize(auxid)}; - const std::string typeName{r.getTypeName(auxid)}; - const bool compressed_high{comp_auxids_high.test(auxid)}; - const bool compressed_low{comp_auxids_low.test(auxid)}; - const bool compressed{compressed_high || compressed_low}; + // Get the element size, type name and compression info for the current auxid + // This information is later passed to the helper function lossyFloatCompress + const size_t eltSize{ r.getEltSize(auxid) }; + const std::string typeName{ r.getTypeName(auxid) }; + const unsigned int nmantissa{ info != nullptr ? info->compression(auxid) : 0 }; // Create the target variable: void* dst = copy.getData (auxid, nremaining, nremaining); @@ -124,27 +115,51 @@ void copyAuxStoreThinned NO_SANITIZE_UNDEFINED for (std::size_t isrc = 0, idst = 0; isrc < size; ++isrc) { if (!dec || !dec->thinned(isrc)) { r.copyForOutput (auxid, dst, idst, src, isrc); - // Compression BEGINS - if (compressed) { - // Get the pointer to the memory - void* eltPtr = reinterpret_cast<char*>(dst) + idst*eltSize; - - // Here comes the actual compression - // By now we should only have either float or std::vector<float> - if(typeName.find("vector") == std::string::npos) { - *(float*) eltPtr = icomp->getCompressedValue(*(float*) eltPtr, compressed_high); - } else { - std::vector<float> &vals = *(reinterpret_cast<std::vector<float>*>(eltPtr)); - for(auto &val: vals) { - val = icomp->getCompressedValue(val, compressed_high); - } - } - } // Compression ENDS + // Apply lossy float compression here (in-place) + // Maybe it would be better to do this via the registry during copy + lossyFloatCompress(dst, idst, eltSize,typeName, nmantissa); ++idst; } } } } +/** + * @brief Helper method to apply lossy float compression + * @param dst Pointer to the start of the vector's data + * @param idst Index of element in vector + * @param eltSize Element size for the auxid + * @param typeName Type name for the auxid + * @param nmantissa Compression level to be used for the auxid + */ +void lossyFloatCompress (void* dst, size_t idst, + const std::size_t& eltSize, + const std::string& typeName, + const unsigned int& nmantissa) +{ + // Check if there is anything to be done + if (nmantissa <= 0) return; + + // Setup all possible compressors: done only once + static const std::vector<CxxUtils::FloatCompressor> compressors = [] { + std::vector<CxxUtils::FloatCompressor> result; + for (std::size_t idx = 0; idx <= 23; ++idx) + result.emplace_back(idx); + return result; + }(); + + // Get the pointer to the memory + void* eltPtr = reinterpret_cast<char*>(dst) + idst*eltSize; + + // This is where we apply in-place lossy float compression + if(typeName == "float") { + *(float*) eltPtr = compressors[nmantissa].reduceFloatPrecision(*(float*) eltPtr); + } else if (typeName == "std::vector<float>"){ + std::vector<float> &vals = *(reinterpret_cast<std::vector<float>*>(eltPtr)); + for(auto &val: vals) { + val = compressors[nmantissa].reduceFloatPrecision(val); + } + } +} } // namespace SG diff --git a/Control/AthContainers/test/copyAuxStoreThinned_test.cxx b/Control/AthContainers/test/copyAuxStoreThinned_test.cxx index 10ba51c6541a..c05328709313 100644 --- a/Control/AthContainers/test/copyAuxStoreThinned_test.cxx +++ b/Control/AthContainers/test/copyAuxStoreThinned_test.cxx @@ -17,6 +17,7 @@ #include "AthContainers/AuxTypeRegistry.h" #include "AthContainers/PackedContainer.h" #include "AthenaKernel/ThinningDecisionBase.h" +#include "CxxUtils/FloatCompressor.h" #include <vector> #include <iostream> #include <cassert> @@ -58,7 +59,7 @@ void compare (const SG::PackedParameters& a, assert (a.scale() == b.scale()); } - + void compare (const SG::AuxStoreInternal& a, const SG::AuxStoreInternal& b, bool thinned = false, @@ -173,10 +174,112 @@ void test1() src.suppress (ftyp); copyAuxStoreThinned (src, dst2, &info); compare (src, dst2, true, ftyp); + +} + +void test2() +{ + std::cout << std::endl; + std::cout << "Testing lossy float compression..." << std::endl; + std::cout << std::endl; + + // Prepare the necessary bits and pieces + SG::ThinningDecisionBase dec; + SG::ThinningInfo info; + info.m_decision = &dec; + dec.resize (5); + dec.buildIndexMap(); + AuxStoreTest src; + + // We want two types: float and std::vector<float> + SG::auxid_t ftyp = SG::AuxTypeRegistry::instance().getAuxID<float> ("aFloat"); + SG::auxid_t fvtyp = SG::AuxTypeRegistry::instance().getAuxID<std::vector<float>> ("aVecFloat"); + + // Get a handle on the underlying data + float* fptr = reinterpret_cast<float*> (src.getData (ftyp, 5, 5)); + std::vector<float>* fvptr = reinterpret_cast<std::vector<float>*> (src.getData (fvtyp, 5, 5)); + + // Fill some random valus + for (int i=0; i < 5; i++) { + fptr[i] = i + 0.512345; + fvptr[i].push_back(i + 0.512345); + fvptr[i].push_back(i*2 + 0.123456); + } + + + // Compress the values w/ nmantissa bits + const unsigned int nmantissa = 7; + info.m_compression[nmantissa].insert(ftyp); + info.m_compression[nmantissa].insert(fvtyp); + SG::AuxStoreInternal dst1; + copyAuxStoreThinned (src, dst1, &info); + + // Retrieve the compressed values + float* rfptr = reinterpret_cast<float*> (dst1.getData (ftyp, 5, 5)); + std::vector<float>* rfvptr = reinterpret_cast<std::vector<float>*> (dst1.getData (fvtyp, 5, 5)); + + // Instentiate a new compressor and check against the result + const CxxUtils::FloatCompressor fc( nmantissa ); + CxxUtils::FloatCompressor::floatint_t uni; + uint32_t result{0}; + + // Helper to pring int in binary w/ a nicer format + auto intToBinStr = [] (uint32_t val) + { + std::string s = std::bitset<32>(val).to_string(); + return (s.substr(0,1) + " " + s.substr(1,8) + " " + s.substr(9,32)); + }; + + // Check the compresion results + for (int i = 0; i < 5; i++) { + std::cout << "Iteration [" << i << "]" << std::endl; + std::cout << std::endl; + + // Testing float + + // Print the original value + uni.fvalue = fptr[i]; + std::cout << "Original value " << std::dec << std::setw( 15 ) << uni.fvalue + << " (binary : " << intToBinStr(uni.ivalue) << ")" << std::endl; + // Print the compressed value + uni.fvalue = rfptr[i]; + std::cout << "Compressed value " << std::dec << std::setw( 15 ) << uni.fvalue + << " (binary : " << intToBinStr(uni.ivalue) << ")" << std::endl; + result = uni.ivalue; + // Check against by-hand compression + uni.fvalue = fc.reduceFloatPrecision( fptr[i] ); + assert( result == uni.ivalue ); + + // Testing std::vector<float> + + std::vector<float>* invec = fvptr + i; + std::vector<float>* outvec = rfvptr + i; + assert(invec != nullptr && outvec != nullptr); + assert(outvec->size() == 2 && outvec->size() == 2); + + for(int j = 0; j < 2; j++) { + // Print the original value + uni.fvalue = invec->at(j); + std::cout << "Original value at [" << j << "] " << std::dec << std::setw( 8 ) << uni.fvalue + << " (binary : " << intToBinStr(uni.ivalue) << ")" << std::endl; + // Print the compressed value + uni.fvalue = outvec->at(j); + std::cout << "Compressed value at [" << j << "] " << std::dec << std::setw( 8 ) << uni.fvalue + << " (binary : " << intToBinStr(uni.ivalue) << ")" << std::endl; + result = uni.ivalue; + // Check against by-hand compression + uni.fvalue = fc.reduceFloatPrecision( invec->at(j) ); + assert( result == uni.ivalue ); + } + + std::cout << std::endl; + } + } int main() { test1(); + test2(); return 0; } -- GitLab From 89280c194c21bf468518a25018283962a96b55a0 Mon Sep 17 00:00:00 2001 From: amete <serhanmete@gmail.com> Date: Wed, 12 Aug 2020 14:20:15 +0200 Subject: [PATCH 2/5] Minor fixes --- Control/AthContainers/src/copyAuxStoreThinned.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Control/AthContainers/src/copyAuxStoreThinned.cxx b/Control/AthContainers/src/copyAuxStoreThinned.cxx index 2bd46dfe8b21..b71c6978cfa5 100644 --- a/Control/AthContainers/src/copyAuxStoreThinned.cxx +++ b/Control/AthContainers/src/copyAuxStoreThinned.cxx @@ -117,7 +117,7 @@ void copyAuxStoreThinned NO_SANITIZE_UNDEFINED r.copyForOutput (auxid, dst, idst, src, isrc); // Apply lossy float compression here (in-place) // Maybe it would be better to do this via the registry during copy - lossyFloatCompress(dst, idst, eltSize,typeName, nmantissa); + lossyFloatCompress(dst, idst, eltSize, typeName, nmantissa); ++idst; } } @@ -138,7 +138,7 @@ void lossyFloatCompress (void* dst, size_t idst, const unsigned int& nmantissa) { // Check if there is anything to be done - if (nmantissa <= 0) return; + if (nmantissa == 0) return; // Setup all possible compressors: done only once static const std::vector<CxxUtils::FloatCompressor> compressors = [] { -- GitLab From d16ac9108d04af97c05fcf1dae8a3d208ba47ad3 Mon Sep 17 00:00:00 2001 From: amete <serhanmete@gmail.com> Date: Wed, 12 Aug 2020 18:34:14 +0200 Subject: [PATCH 3/5] Bugfix vector size check --- Control/AthContainers/test/copyAuxStoreThinned_test.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Control/AthContainers/test/copyAuxStoreThinned_test.cxx b/Control/AthContainers/test/copyAuxStoreThinned_test.cxx index c05328709313..db2a83780a5b 100644 --- a/Control/AthContainers/test/copyAuxStoreThinned_test.cxx +++ b/Control/AthContainers/test/copyAuxStoreThinned_test.cxx @@ -255,7 +255,7 @@ void test2() std::vector<float>* invec = fvptr + i; std::vector<float>* outvec = rfvptr + i; assert(invec != nullptr && outvec != nullptr); - assert(outvec->size() == 2 && outvec->size() == 2); + assert(invec->size() == 2 && outvec->size() == 2); for(int j = 0; j < 2; j++) { // Print the original value -- GitLab From 3677d9fa7a08ad68ddbce6cf485c69481648640f Mon Sep 17 00:00:00 2001 From: amete <serhanmete@gmail.com> Date: Thu, 13 Aug 2020 10:57:54 +0200 Subject: [PATCH 4/5] A few minor improvements --- .../AthContainers/tools/copyAuxStoreThinned.h | 2 +- Control/AthContainers/src/copyAuxStoreThinned.cxx | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h b/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h index ac10b6af72bf..daff78e7b32a 100644 --- a/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h +++ b/Control/AthContainers/AthContainers/tools/copyAuxStoreThinned.h @@ -48,7 +48,7 @@ void copyAuxStoreThinned (const SG::IConstAuxStore& orig, * @param typeName Type name for the auxid * @param nmantissa Compression level to be used for the auxid */ -void lossyFloatCompress (void* dst, size_t dst_index, +void lossyFloatCompress (void* dst, std::size_t dst_index, const std::size_t& eltSize, const std::string& typeName, const unsigned int& nmantissa); diff --git a/Control/AthContainers/src/copyAuxStoreThinned.cxx b/Control/AthContainers/src/copyAuxStoreThinned.cxx index b71c6978cfa5..3af0822cd6d2 100644 --- a/Control/AthContainers/src/copyAuxStoreThinned.cxx +++ b/Control/AthContainers/src/copyAuxStoreThinned.cxx @@ -132,18 +132,22 @@ void copyAuxStoreThinned NO_SANITIZE_UNDEFINED * @param typeName Type name for the auxid * @param nmantissa Compression level to be used for the auxid */ -void lossyFloatCompress (void* dst, size_t idst, +void lossyFloatCompress (void* dst, std::size_t idst, const std::size_t& eltSize, const std::string& typeName, const unsigned int& nmantissa) { // Check if there is anything to be done - if (nmantissa == 0) return; + // Total number of explicit mantissa bits for a 32 bit float is 23 + // 0 is used to denote no compression should be applied to the variable + static const unsigned int NMANTISSA_MAX = 23; + if (nmantissa == 0 || nmantissa >= NMANTISSA_MAX) return; // Setup all possible compressors: done only once static const std::vector<CxxUtils::FloatCompressor> compressors = [] { std::vector<CxxUtils::FloatCompressor> result; - for (std::size_t idx = 0; idx <= 23; ++idx) + result.reserve(NMANTISSA_MAX); + for (std::size_t idx = 0; idx < NMANTISSA_MAX; ++idx) result.emplace_back(idx); return result; }(); -- GitLab From 645e640aca5a472b3a8b4a806d42e6449ff0a857 Mon Sep 17 00:00:00 2001 From: amete <serhanmete@gmail.com> Date: Thu, 13 Aug 2020 17:56:16 +0200 Subject: [PATCH 5/5] Fix the typo in the comments --- Control/AthContainers/test/copyAuxStoreThinned_test.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Control/AthContainers/test/copyAuxStoreThinned_test.cxx b/Control/AthContainers/test/copyAuxStoreThinned_test.cxx index db2a83780a5b..9d75255af87d 100644 --- a/Control/AthContainers/test/copyAuxStoreThinned_test.cxx +++ b/Control/AthContainers/test/copyAuxStoreThinned_test.cxx @@ -223,7 +223,7 @@ void test2() CxxUtils::FloatCompressor::floatint_t uni; uint32_t result{0}; - // Helper to pring int in binary w/ a nicer format + // Helper to print int in binary w/ a nicer format auto intToBinStr = [] (uint32_t val) { std::string s = std::bitset<32>(val).to_string(); -- GitLab