Coverage Report

Created: 2026-07-23 08:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/byte_stream_split.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "util/byte_stream_split.h"
19
20
#include <glog/logging.h>
21
22
#include <array>
23
#include <bit> // IWYU pragma: keep
24
#include <cstring>
25
#include <vector>
26
27
namespace doris {
28
29
inline void do_merge_streams(const uint8_t** src_streams, int width, int64_t nvalues,
30
42
                             uint8_t* dest) {
31
    // Value empirically chosen to provide the best performance on the author's machine
32
42
    constexpr int kBlockSize = 128;
33
34
8.23k
    while (nvalues >= kBlockSize) {
35
40.9k
        for (int stream = 0; stream < width; ++stream) {
36
            // Take kBlockSize bytes from the given stream and spread them
37
            // to their logical places in destination.
38
32.7k
            const uint8_t* src = src_streams[stream];
39
557k
            for (int i = 0; i < kBlockSize; i += 8) {
40
524k
                uint64_t v;
41
524k
                std::memcpy(&v, src + i, sizeof(v));
42
524k
                if constexpr (std::endian::native == std::endian::little) {
43
524k
                    dest[stream + i * width] = static_cast<uint8_t>(v);
44
524k
                    dest[stream + (i + 1) * width] = static_cast<uint8_t>(v >> 8);
45
524k
                    dest[stream + (i + 2) * width] = static_cast<uint8_t>(v >> 16);
46
524k
                    dest[stream + (i + 3) * width] = static_cast<uint8_t>(v >> 24);
47
524k
                    dest[stream + (i + 4) * width] = static_cast<uint8_t>(v >> 32);
48
524k
                    dest[stream + (i + 5) * width] = static_cast<uint8_t>(v >> 40);
49
524k
                    dest[stream + (i + 6) * width] = static_cast<uint8_t>(v >> 48);
50
524k
                    dest[stream + (i + 7) * width] = static_cast<uint8_t>(v >> 56);
51
                } else if constexpr (std::endian::native == std::endian::big) {
52
                    dest[stream + i * width] = static_cast<uint8_t>(v >> 56);
53
                    dest[stream + (i + 1) * width] = static_cast<uint8_t>(v >> 48);
54
                    dest[stream + (i + 2) * width] = static_cast<uint8_t>(v >> 40);
55
                    dest[stream + (i + 3) * width] = static_cast<uint8_t>(v >> 32);
56
                    dest[stream + (i + 4) * width] = static_cast<uint8_t>(v >> 24);
57
                    dest[stream + (i + 5) * width] = static_cast<uint8_t>(v >> 16);
58
                    dest[stream + (i + 6) * width] = static_cast<uint8_t>(v >> 8);
59
                    dest[stream + (i + 7) * width] = static_cast<uint8_t>(v);
60
                }
61
524k
            }
62
32.7k
            src_streams[stream] += kBlockSize;
63
32.7k
        }
64
8.19k
        dest += width * kBlockSize;
65
8.19k
        nvalues -= kBlockSize;
66
8.19k
    }
67
68
    // Epilog
69
259
    for (int stream = 0; stream < width; ++stream) {
70
217
        const uint8_t* src = src_streams[stream];
71
2.10k
        for (int64_t i = 0; i < nvalues; ++i) {
72
1.88k
            dest[stream + i * width] = src[i];
73
1.88k
        }
74
217
    }
75
42
}
76
77
template <int kNumStreams>
78
void byte_stream_split_decode_scalar(const uint8_t* src, int width, int64_t offset,
79
39
                                     int64_t num_values, int64_t stride, uint8_t* dest) {
80
39
    DCHECK(width == kNumStreams);
81
39
    std::array<const uint8_t*, kNumStreams> src_streams;
82
245
    for (int stream = 0; stream < kNumStreams; ++stream) {
83
206
        src_streams[stream] = &src[stream * stride + offset];
84
206
    }
85
39
    do_merge_streams(src_streams.data(), kNumStreams, num_values, dest);
86
39
}
_ZN5doris31byte_stream_split_decode_scalarILi2EEEvPKhilllPh
Line
Count
Source
79
3
                                     int64_t num_values, int64_t stride, uint8_t* dest) {
80
3
    DCHECK(width == kNumStreams);
81
3
    std::array<const uint8_t*, kNumStreams> src_streams;
82
9
    for (int stream = 0; stream < kNumStreams; ++stream) {
83
6
        src_streams[stream] = &src[stream * stride + offset];
84
6
    }
85
3
    do_merge_streams(src_streams.data(), kNumStreams, num_values, dest);
86
3
}
_ZN5doris31byte_stream_split_decode_scalarILi4EEEvPKhilllPh
Line
Count
Source
79
22
                                     int64_t num_values, int64_t stride, uint8_t* dest) {
80
22
    DCHECK(width == kNumStreams);
81
22
    std::array<const uint8_t*, kNumStreams> src_streams;
82
110
    for (int stream = 0; stream < kNumStreams; ++stream) {
83
88
        src_streams[stream] = &src[stream * stride + offset];
84
88
    }
85
22
    do_merge_streams(src_streams.data(), kNumStreams, num_values, dest);
86
22
}
_ZN5doris31byte_stream_split_decode_scalarILi8EEEvPKhilllPh
Line
Count
Source
79
14
                                     int64_t num_values, int64_t stride, uint8_t* dest) {
80
14
    DCHECK(width == kNumStreams);
81
14
    std::array<const uint8_t*, kNumStreams> src_streams;
82
126
    for (int stream = 0; stream < kNumStreams; ++stream) {
83
112
        src_streams[stream] = &src[stream * stride + offset];
84
112
    }
85
14
    do_merge_streams(src_streams.data(), kNumStreams, num_values, dest);
86
14
}
Unexecuted instantiation: _ZN5doris31byte_stream_split_decode_scalarILi16EEEvPKhilllPh
87
88
inline void byte_stream_split_decode_scalar_dynamic(const uint8_t* src, int width, int64_t offset,
89
                                                    int64_t num_values, int64_t stride,
90
3
                                                    uint8_t* dest) {
91
3
    std::vector<const uint8_t*> src_streams;
92
3
    src_streams.resize(width);
93
14
    for (int stream = 0; stream < width; ++stream) {
94
11
        src_streams[stream] = &src[stream * stride + offset];
95
11
    }
96
3
    do_merge_streams(src_streams.data(), width, num_values, dest);
97
3
}
98
99
// TODO: optimize using simd: https://github.com/apache/arrow/pull/38529
100
void byte_stream_split_decode(const uint8_t* src, int width, int64_t offset, int64_t num_values,
101
44
                              int64_t stride, uint8_t* dest) {
102
44
    switch (width) {
103
2
    case 1:
104
2
        memcpy(dest, src + offset * width, num_values);
105
2
        return;
106
3
    case 2:
107
3
        return byte_stream_split_decode_scalar<2>(src, width, offset, num_values, stride, dest);
108
22
    case 4:
109
22
        return byte_stream_split_decode_scalar<4>(src, width, offset, num_values, stride, dest);
110
14
    case 8:
111
14
        return byte_stream_split_decode_scalar<8>(src, width, offset, num_values, stride, dest);
112
0
    case 16:
113
0
        return byte_stream_split_decode_scalar<16>(src, width, offset, num_values, stride, dest);
114
44
    }
115
3
    return byte_stream_split_decode_scalar_dynamic(src, width, offset, num_values, stride, dest);
116
44
}
117
118
} // namespace doris