LCOV - code coverage report
Current view: top level - src/storage - DataLayout.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 56 126 44.4 %
Date: 2025-03-25 01:19:55 Functions: 7 16 43.8 %
Branches: 50 128 39.1 %

           Branch data     Line data    Source code
       1                 :            : #include <mutable/storage/DataLayout.hpp>
       2                 :            : 
       3                 :            : #include <mutable/catalog/Schema.hpp>
       4                 :            : #include <mutable/catalog/Type.hpp>
       5                 :            : 
       6                 :            : 
       7                 :            : using namespace m;
       8                 :            : using namespace m::storage;
       9                 :            : 
      10                 :            : 
      11                 :            : /*----------------------------------------------------------------------------------------------------------------------
      12                 :            :  * DataLayout::Node
      13                 :            :  *--------------------------------------------------------------------------------------------------------------------*/
      14                 :            : 
      15                 :       2813 : DataLayout::Node::~Node() { }
      16                 :            : 
      17                 :            : 
      18                 :            : /*----------------------------------------------------------------------------------------------------------------------
      19                 :            :  * DataLayout::Leaf
      20                 :            :  *--------------------------------------------------------------------------------------------------------------------*/
      21                 :            : 
      22                 :          0 : void DataLayout::Leaf::accept(ConstDataLayoutVisitor &v) const { v(*this); };
      23                 :            : 
      24                 :            : 
      25                 :            : /*----------------------------------------------------------------------------------------------------------------------
      26                 :            :  * DataLayout::INode
      27                 :            :  *--------------------------------------------------------------------------------------------------------------------*/
      28                 :            : 
      29                 :       1286 : DataLayout::Leaf & DataLayout::INode::add_leaf(const m::Type *type, size_type idx,
      30                 :            :                                                uint64_t offset_in_bits, uint64_t stride_in_bits)
      31                 :            : {
      32         [ +  + ]:       1286 :     M_insist(this->num_tuples() != 1 or stride_in_bits == 0, "no stride without repetition");
      33   [ +  +  +  + ]:       1286 :     M_insist(stride_in_bits % 8 == 0 or type->is_boolean() or type->is_bitmap(),
      34                 :            :              "only booleans and bitmaps may not be byte aligned");
      35   [ +  +  +  + ]:       1286 :     M_insist(offset_in_bits % 8 == 0 or type->is_boolean() or type->is_bitmap(),
      36                 :            :              "only booleans and bitmaps may not be byte aligned");
      37                 :            : 
      38         [ +  - ]:       1286 :     auto leaf = new Leaf(type, idx);
      39   [ +  -  +  -  :       5144 :     children_.emplace_back(child_t{
             +  -  +  - ]
      40                 :       1286 :         .ptr = std::unique_ptr<DataLayout::Node>(as<Node>(leaf)),
      41                 :       1286 :         .offset_in_bits = offset_in_bits,
      42                 :       1286 :         .stride_in_bits = stride_in_bits,
      43                 :            :     });
      44                 :       1286 :     return *leaf;
      45                 :          0 : }
      46                 :            : 
      47                 :          4 : DataLayout::INode & DataLayout::INode::add_inode(size_type num_tuples, uint64_t offset_in_bits, uint64_t stride_in_bits)
      48                 :            : {
      49                 :          4 :     M_insist(num_tuples != 0, "the new INode must be large enough for at least one tuple");
      50         [ +  - ]:          4 :     M_insist(this->num_tuples() != 1 or stride_in_bits == 0, "no stride without repetition");
      51                 :          4 :     M_insist(this->num_tuples() % num_tuples == 0,
      52                 :            :              "the number of tuples in the parent must be a whole multiple of the number of tuples of the newly created "
      53                 :            :              "INode");
      54                 :          4 :     M_insist(offset_in_bits % 8 == 0, "the offset of the newly created INode must be byte aligned");
      55                 :          4 :     M_insist(stride_in_bits % 8 == 0, "the stride of the newly created INode must be byte aligned");
      56                 :            : 
      57         [ +  - ]:          4 :     auto inode = new INode(num_tuples);
      58   [ +  -  +  -  :         16 :     children_.emplace_back(child_t{
             +  -  +  - ]
      59                 :          4 :         .ptr = std::unique_ptr<DataLayout::Node>(as<Node>(inode)),
      60                 :          4 :         .offset_in_bits = offset_in_bits,
      61                 :          4 :         .stride_in_bits = stride_in_bits,
      62                 :            :     });
      63                 :          4 :     return *inode;
      64                 :          0 : }
      65                 :            : 
      66                 :          0 : void DataLayout::INode::accept(ConstDataLayoutVisitor &v) const { v(*this); }
      67                 :            : 
      68                 :         26 : void DataLayout::INode::for_sibling_leaves(level_info_stack_t &level_info_stack, uint64_t inode_offset_in_bits,
      69                 :            :                                            const callback_leaves_t &callback) const
      70                 :            : {
      71                 :         26 :     std::vector<leaf_info_t> leaves;
      72   [ +  -  +  - ]:         26 :     leaves.reserve(this->num_children());
      73                 :            : 
      74   [ +  -  +  -  :        118 :     for (auto &child : *this) {
          +  -  +  +  +  
                -  +  - ]
      75   [ +  -  +  + ]:         91 :         if (auto child_leaf = cast<const Leaf>(child.ptr.get())) {
      76   [ +  -  +  -  :        304 :             leaves.emplace_back(leaf_info_t{
             +  -  +  - ]
      77                 :         76 :                 .leaf = *child_leaf,
      78                 :         76 :                 .offset_in_bits = child.offset_in_bits,
      79                 :         76 :                 .stride_in_bits = child.stride_in_bits,
      80                 :            :             });
      81                 :         76 :         } else {
      82         [ +  - ]:         15 :             auto child_inode = as<const INode>(child.ptr.get());
      83         [ +  - ]:         30 :             level_info_stack.emplace_back(level_info_t{
      84                 :         15 :                 .stride_in_bits = child.stride_in_bits,
      85         [ +  - ]:         15 :                 .num_tuples = child_inode->num_tuples(),
      86                 :            :             });
      87         [ +  - ]:         15 :             child_inode->for_sibling_leaves(level_info_stack, inode_offset_in_bits + child.offset_in_bits, callback);
      88                 :         15 :             level_info_stack.pop_back();
      89                 :            :         }
      90                 :            :     }
      91                 :            : 
      92         [ +  + ]:         26 :     if (not leaves.empty())
      93         [ +  - ]:         13 :         callback(leaves, level_info_stack, inode_offset_in_bits);
      94                 :         26 : }
      95                 :            : 
      96                 :            : M_LCOV_EXCL_START
      97                 :            : namespace {
      98                 :            : 
      99                 :            : /** Start a new line with proper indentation. */
     100                 :            : std::ostream & indent(std::ostream &out, unsigned indentation)
     101                 :            : {
     102                 :            :     out << '\n' << std::string(4 * indentation, ' ');
     103                 :            :     return out;
     104                 :            : }
     105                 :            : 
     106                 :            : }
     107                 :            : 
     108                 :            : void DataLayout::INode::print(std::ostream &out, unsigned int indentation) const
     109                 :            : {
     110                 :            :     const std::size_t decimal_places = std::ceil(num_children() / Numeric::DECIMAL_TO_BINARY_DIGITS);
     111                 :            : 
     112                 :            :     auto it = cbegin();
     113                 :            :     for (std::size_t i = 0; i != num_children(); ++i, ++it) {
     114                 :            :         M_insist(it != cend());
     115                 :            : 
     116                 :            :         indent(out, indentation) << "[" << std::setw(decimal_places) << i << "]: ";
     117                 :            : 
     118                 :            :         auto &child = *it;
     119                 :            :         if (auto child_leaf = cast<const Leaf>(child.ptr.get())) {
     120                 :            :             out << "Leaf " << child_leaf->index() << " of type " << *child_leaf->type() << " with bit offset "
     121                 :            :                 << child.offset_in_bits << " and bit stride " << child.stride_in_bits;
     122                 :            :         } else {
     123                 :            :             auto child_inode = as<const INode>(child.ptr.get());
     124                 :            :             out << "INode of " << child_inode->num_tuples() << " tuple(s) with bit offset " << child.offset_in_bits
     125                 :            :                 << " and bit stride " << child.stride_in_bits;
     126                 :            :             child_inode->print(out, indentation + 1);
     127                 :            :         }
     128                 :            :     }
     129                 :            : }
     130                 :            : M_LCOV_EXCL_STOP
     131                 :            : 
     132                 :            : 
     133                 :            : /*----------------------------------------------------------------------------------------------------------------------
     134                 :            :  * DataLayout
     135                 :            :  *--------------------------------------------------------------------------------------------------------------------*/
     136                 :            : 
     137                 :          0 : DataLayout::Leaf & DataLayout::add_leaf(const m::Type *type, size_type idx, uint64_t stride_in_bits)
     138                 :            : {
     139                 :          0 :     M_insist(inode_.num_children() == 0, "child already set");
     140                 :          0 :     return inode_.add_leaf(type, idx, 0, stride_in_bits);
     141                 :            : }
     142                 :            : 
     143                 :        344 : DataLayout::INode & DataLayout::add_inode(size_type num_tuples, uint64_t stride_in_bits)
     144                 :            : {
     145                 :        344 :     M_insist(inode_.num_children() == 0, "child already set");
     146                 :        344 :     M_insist(num_tuples != 0, "the new INode must be large enough for at least one tuple");
     147         [ +  - ]:        344 :     M_insist(inode_.num_tuples() != 1 or stride_in_bits == 0, "no stride without repetition");
     148                 :        344 :     M_insist(stride_in_bits % 8 == 0, "the stride of the newly created INode must be byte aligned");
     149                 :            : 
     150         [ +  - ]:        344 :     auto inode = new INode(num_tuples);
     151   [ +  -  +  -  :       1032 :     inode_.children_.emplace_back(INode::child_t{
                   +  - ]
     152                 :        344 :         .ptr = std::unique_ptr<DataLayout::Node>(as<Node>(inode)),
     153                 :            :         .offset_in_bits = 0,
     154                 :        344 :         .stride_in_bits = stride_in_bits,
     155                 :            :     });
     156                 :        344 :     return *inode;
     157                 :          0 : }
     158                 :            : 
     159                 :          0 : void DataLayout::accept(ConstDataLayoutVisitor &v) const { v(*this); }
     160                 :            : 
     161                 :         11 : void DataLayout::for_sibling_leaves(DataLayout::callback_leaves_t callback) const
     162                 :            : {
     163                 :         11 :     level_info_stack_t level_info_stack;
     164         [ +  - ]:         11 :     inode_.for_sibling_leaves(level_info_stack, 0, callback);
     165                 :         11 : }
     166                 :            : 
     167                 :            : 
     168                 :            : /*======================================================================================================================
     169                 :            :  * Helper functions for SIMD support
     170                 :            :  *====================================================================================================================*/
     171                 :            : 
     172                 :          0 : bool m::storage::supports_simd(const DataLayout &layout, const Schema &layout_schema, const Schema &tuple_schema)
     173                 :            : {
     174                 :          0 :     const bool needs_null_bitmap = [&]() {
     175         [ #  # ]:          0 :         for (auto &tuple_entry : tuple_schema) {
     176         [ #  # ]:          0 :             if (layout_schema[tuple_entry.id].second.nullable())
     177                 :          0 :                 return true; // found an entry in `tuple_schema` that can be NULL according to `layout_schema`
     178                 :            :         }
     179                 :          0 :         return false; // no attribute in `tuple_schema` can be NULL according to `layout_schema`
     180                 :          0 :     }();
     181                 :            : 
     182                 :          0 :     auto test_simd_support = [&](const DataLayout::INode& inode, auto &rec) {
     183         [ #  # ]:          0 :         if (inode.num_children() == 0)
     184                 :          0 :             return false; // invalid data layout
     185                 :            : 
     186         [ #  # ]:          0 :         if (auto child_inode = cast<const DataLayout::INode>(inode[0].ptr.get());
     187         [ #  # ]:          0 :             inode.num_children() == 1 and child_inode)
     188                 :            :         {
     189                 :          0 :             return rec(*child_inode, rec); // recurse for single INode child
     190                 :            :         }
     191                 :            : 
     192                 :          0 :         std::size_t num_simd_lanes = 1;
     193         [ #  # ]:          0 :         for (auto &child : inode) {
     194         [ #  # ]:          0 :             if (cast<const DataLayout::INode>(child.ptr.get()))
     195                 :          0 :                 return false; // multiple INode children or mixed Leaf and INode children
     196                 :            : 
     197                 :          0 :             auto &child_leaf = as<const DataLayout::Leaf>(*child.ptr);
     198                 :          0 :             const uint8_t bit_stride = child.stride_in_bits % 8;
     199         [ #  # ]:          0 :             if (child_leaf.index() == layout_schema.num_entries()) { // NULL bitmap
     200         [ #  # ]:          0 :                 if (not needs_null_bitmap)
     201                 :          0 :                     continue; // no NULL bitmap needed
     202                 :            : 
     203         [ #  # ]:          0 :                 if (bit_stride) {
     204                 :          0 :                     return false; // NULL bitmap with bit stride currently not supported
     205                 :            :                 } else {
     206         [ #  # ]:          0 :                     if (tuple_schema.num_entries() > 64)
     207                 :          0 :                         return false; // bytes containing a NULL bitmap must fit into scalar value
     208         [ #  # ]:          0 :                     if (std::max(ceil_to_pow_2(tuple_schema.num_entries()), 8UL) != child.stride_in_bits)
     209                 :          0 :                         return false; // distance between two NULL bits of a single attribute must be a power of 2
     210         [ #  # ]:          0 :                     if (child.offset_in_bits % 8 != 0)
     211                 :          0 :                         return false; // NULL bitmaps must not start with bit offset
     212                 :            :                 }
     213                 :            : 
     214                 :          0 :                 num_simd_lanes = std::max<std::size_t>(num_simd_lanes, 16); // repeat 16 times for 128bit SIMD vectors
     215                 :          0 :             } else { // regular entry
     216                 :          0 :                 auto tuple_it = tuple_schema.find(layout_schema[child_leaf.index()].id);
     217         [ #  # ]:          0 :                 if (tuple_it == tuple_schema.end())
     218                 :          0 :                     continue; // entry not contained in tuple schema
     219                 :          0 :                 M_insist(*tuple_it->type == *child_leaf.type());
     220                 :            : 
     221         [ #  # ]:          0 :                 if (bit_stride) {
     222         [ #  # ]:          0 :                     if (child.stride_in_bits != 1)
     223                 :          0 :                         return false; // stride must be 1 bit
     224                 :          0 :                 } else {
     225   [ #  #  #  # ]:          0 :                     if (tuple_it->type->is_boolean() and child.stride_in_bits != 8)
     226                 :          0 :                         return false; // booleans must be packed consecutively in bytes
     227         [ #  # ]:          0 :                     if (tuple_it->type->is_character_sequence())
     228                 :          0 :                         return false; // string SIMDfication currently not supported
     229                 :            :                 }
     230                 :            : 
     231                 :          0 :                 const uint64_t size_in_bytes = (tuple_it->type->size() + 7) / 8;
     232                 :          0 :                 num_simd_lanes = std::max<std::size_t>(num_simd_lanes, 16 / size_in_bytes); // repeat to fill 128bit SIMD vector
     233                 :            :             }
     234                 :            :         }
     235                 :            : 
     236         [ #  # ]:          0 :         if (inode.num_tuples() % num_simd_lanes != 0)
     237                 :          0 :             return false; // number of tuples in INode must be a whole multiple of SIMD width
     238                 :            : 
     239                 :          0 :         return true; // fallthrough
     240                 :          0 :     };
     241                 :          0 :     return test_simd_support(static_cast<const DataLayout::INode&>(layout), test_simd_support);
     242                 :            : }
     243                 :            : 
     244                 :          0 : std::size_t m::storage::get_num_simd_lanes(const DataLayout &layout, const Schema &layout_schema,
     245                 :            :                                            const Schema &tuple_schema)
     246                 :            : {
     247                 :          0 :     M_insist(supports_simd(layout, layout_schema, tuple_schema),
     248                 :            :              "layout must support SIMD to retrieve its number of SIMD lanes");
     249                 :            : 
     250                 :          0 :     std::size_t num_simd_lanes = 1;
     251         [ #  # ]:          0 :     for (auto &tuple_entry : tuple_schema) {
     252         [ #  # ]:          0 :         if (layout_schema[tuple_entry.id].second.nullable())
     253                 :          0 :             return 16; // repeat 16 times for 128bit SIMD vector; return immediately since this is the max. #lanes
     254                 :            : 
     255                 :          0 :         const uint64_t size_in_bytes = (tuple_entry.type->size() + 7) / 8;
     256                 :          0 :         num_simd_lanes = std::max<std::size_t>(num_simd_lanes, 16 / size_in_bytes); // repeat to fill 128bit SIMD vector
     257                 :            :     }
     258                 :            : 
     259                 :          0 :     return num_simd_lanes;
     260                 :          0 : }

Generated by: LCOV version 1.16