Coverage Report

Created: 2026-07-29 09:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/simd/parquet_kernels.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/simd/parquet_kernels.h"
19
20
#include <array>
21
#include <bit>
22
#include <cmath>
23
#include <cstring>
24
#include <type_traits>
25
26
#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))
27
#include <immintrin.h>
28
#define DORIS_PARQUET_X86_SIMD
29
#endif
30
31
namespace doris::simd {
32
namespace {
33
34
#ifdef DORIS_PARQUET_X86_SIMD
35
// Keep x86-only dispatch helpers out of scalar-only builds so warning-clean ARM builds do not
36
// depend on compiler-specific unused-function behavior.
37
4.29M
bool has_avx2() {
38
4.29M
    return __builtin_cpu_supports("avx2");
39
4.29M
}
40
41
void byte_stream_split_decode_scalar(const uint8_t* src, size_t width, size_t offset,
42
19
                                     size_t num_values, size_t stride, uint8_t* dest) {
43
27
    for (size_t row = 0; row < num_values; ++row) {
44
56
        for (size_t byte = 0; byte < width; ++byte) {
45
48
            dest[row * width + byte] = src[byte * stride + offset + row];
46
48
        }
47
8
    }
48
19
}
49
#endif
50
51
template <typename T>
52
2.05k
bool scalar_compare(T lhs, T rhs, RawComparisonOp op) {
53
2.05k
    const auto equal = [](T left, T right) {
54
929
        if constexpr (std::is_floating_point_v<T>) {
55
23
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
23
        }
57
0
        return left == right;
58
929
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIfEEbT_S3_NS0_15RawComparisonOpEENKUlffE_clEff
Line
Count
Source
53
11
    const auto equal = [](T left, T right) {
54
11
        if constexpr (std::is_floating_point_v<T>) {
55
11
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
11
        }
57
0
        return left == right;
58
11
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIdEEbT_S3_NS0_15RawComparisonOpEENKUlddE_clEdd
Line
Count
Source
53
12
    const auto equal = [](T left, T right) {
54
12
        if constexpr (std::is_floating_point_v<T>) {
55
12
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
12
        }
57
0
        return left == right;
58
12
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIiEEbT_S3_NS0_15RawComparisonOpEENKUliiE_clEii
Line
Count
Source
53
846
    const auto equal = [](T left, T right) {
54
        if constexpr (std::is_floating_point_v<T>) {
55
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
        }
57
846
        return left == right;
58
846
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIlEEbT_S3_NS0_15RawComparisonOpEENKUlllE_clEll
Line
Count
Source
53
60
    const auto equal = [](T left, T right) {
54
        if constexpr (std::is_floating_point_v<T>) {
55
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
        }
57
60
        return left == right;
58
60
    };
59
2.05k
    const auto greater = [](T left, T right) {
60
1.12k
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
104
            if (std::isnan(right)) {
63
3
                return false;
64
3
            }
65
101
            if (std::isnan(left)) {
66
2
                return true;
67
2
            }
68
101
        }
69
99
        return left > right;
70
1.12k
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIfEEbT_S3_NS0_15RawComparisonOpEENKUlffE0_clEff
Line
Count
Source
59
18
    const auto greater = [](T left, T right) {
60
18
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
18
            if (std::isnan(right)) {
63
3
                return false;
64
3
            }
65
15
            if (std::isnan(left)) {
66
2
                return true;
67
2
            }
68
15
        }
69
13
        return left > right;
70
18
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIdEEbT_S3_NS0_15RawComparisonOpEENKUlddE0_clEdd
Line
Count
Source
59
86
    const auto greater = [](T left, T right) {
60
86
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
86
            if (std::isnan(right)) {
63
0
                return false;
64
0
            }
65
86
            if (std::isnan(left)) {
66
0
                return true;
67
0
            }
68
86
        }
69
86
        return left > right;
70
86
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIiEEbT_S3_NS0_15RawComparisonOpEENKUliiE0_clEii
Line
Count
Source
59
928
    const auto greater = [](T left, T right) {
60
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
            if (std::isnan(right)) {
63
                return false;
64
            }
65
            if (std::isnan(left)) {
66
                return true;
67
            }
68
        }
69
928
        return left > right;
70
928
    };
parquet_kernels.cpp:_ZZN5doris4simd12_GLOBAL__N_114scalar_compareIlEEbT_S3_NS0_15RawComparisonOpEENKUlllE0_clEll
Line
Count
Source
59
94
    const auto greater = [](T left, T right) {
60
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
            if (std::isnan(right)) {
63
                return false;
64
            }
65
            if (std::isnan(left)) {
66
                return true;
67
            }
68
        }
69
94
        return left > right;
70
94
    };
71
2.05k
    switch (op) {
72
929
    case RawComparisonOp::EQ:
73
929
        return equal(lhs, rhs);
74
0
    case RawComparisonOp::NE:
75
0
        return !equal(lhs, rhs);
76
57
    case RawComparisonOp::LT:
77
57
        return greater(rhs, lhs);
78
151
    case RawComparisonOp::LE:
79
151
        return !greater(lhs, rhs);
80
636
    case RawComparisonOp::GT:
81
636
        return greater(lhs, rhs);
82
282
    case RawComparisonOp::GE:
83
282
        return !greater(rhs, lhs);
84
2.05k
    }
85
0
    __builtin_unreachable();
86
2.05k
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_114scalar_compareIfEEbT_S3_NS0_15RawComparisonOpE
Line
Count
Source
52
29
bool scalar_compare(T lhs, T rhs, RawComparisonOp op) {
53
29
    const auto equal = [](T left, T right) {
54
29
        if constexpr (std::is_floating_point_v<T>) {
55
29
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
29
        }
57
29
        return left == right;
58
29
    };
59
29
    const auto greater = [](T left, T right) {
60
29
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
29
            if (std::isnan(right)) {
63
29
                return false;
64
29
            }
65
29
            if (std::isnan(left)) {
66
29
                return true;
67
29
            }
68
29
        }
69
29
        return left > right;
70
29
    };
71
29
    switch (op) {
72
11
    case RawComparisonOp::EQ:
73
11
        return equal(lhs, rhs);
74
0
    case RawComparisonOp::NE:
75
0
        return !equal(lhs, rhs);
76
8
    case RawComparisonOp::LT:
77
8
        return greater(rhs, lhs);
78
3
    case RawComparisonOp::LE:
79
3
        return !greater(lhs, rhs);
80
7
    case RawComparisonOp::GT:
81
7
        return greater(lhs, rhs);
82
0
    case RawComparisonOp::GE:
83
0
        return !greater(rhs, lhs);
84
29
    }
85
0
    __builtin_unreachable();
86
29
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_114scalar_compareIdEEbT_S3_NS0_15RawComparisonOpE
Line
Count
Source
52
98
bool scalar_compare(T lhs, T rhs, RawComparisonOp op) {
53
98
    const auto equal = [](T left, T right) {
54
98
        if constexpr (std::is_floating_point_v<T>) {
55
98
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
98
        }
57
98
        return left == right;
58
98
    };
59
98
    const auto greater = [](T left, T right) {
60
98
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
98
            if (std::isnan(right)) {
63
98
                return false;
64
98
            }
65
98
            if (std::isnan(left)) {
66
98
                return true;
67
98
            }
68
98
        }
69
98
        return left > right;
70
98
    };
71
98
    switch (op) {
72
12
    case RawComparisonOp::EQ:
73
12
        return equal(lhs, rhs);
74
0
    case RawComparisonOp::NE:
75
0
        return !equal(lhs, rhs);
76
0
    case RawComparisonOp::LT:
77
0
        return greater(rhs, lhs);
78
0
    case RawComparisonOp::LE:
79
0
        return !greater(lhs, rhs);
80
86
    case RawComparisonOp::GT:
81
86
        return greater(lhs, rhs);
82
0
    case RawComparisonOp::GE:
83
0
        return !greater(rhs, lhs);
84
98
    }
85
0
    __builtin_unreachable();
86
98
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_114scalar_compareIiEEbT_S3_NS0_15RawComparisonOpE
Line
Count
Source
52
1.77k
bool scalar_compare(T lhs, T rhs, RawComparisonOp op) {
53
1.77k
    const auto equal = [](T left, T right) {
54
1.77k
        if constexpr (std::is_floating_point_v<T>) {
55
1.77k
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
1.77k
        }
57
1.77k
        return left == right;
58
1.77k
    };
59
1.77k
    const auto greater = [](T left, T right) {
60
1.77k
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
1.77k
            if (std::isnan(right)) {
63
1.77k
                return false;
64
1.77k
            }
65
1.77k
            if (std::isnan(left)) {
66
1.77k
                return true;
67
1.77k
            }
68
1.77k
        }
69
1.77k
        return left > right;
70
1.77k
    };
71
1.77k
    switch (op) {
72
846
    case RawComparisonOp::EQ:
73
846
        return equal(lhs, rhs);
74
0
    case RawComparisonOp::NE:
75
0
        return !equal(lhs, rhs);
76
49
    case RawComparisonOp::LT:
77
49
        return greater(rhs, lhs);
78
112
    case RawComparisonOp::LE:
79
112
        return !greater(lhs, rhs);
80
535
    case RawComparisonOp::GT:
81
535
        return greater(lhs, rhs);
82
232
    case RawComparisonOp::GE:
83
232
        return !greater(rhs, lhs);
84
1.77k
    }
85
0
    __builtin_unreachable();
86
1.77k
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_114scalar_compareIlEEbT_S3_NS0_15RawComparisonOpE
Line
Count
Source
52
154
bool scalar_compare(T lhs, T rhs, RawComparisonOp op) {
53
154
    const auto equal = [](T left, T right) {
54
154
        if constexpr (std::is_floating_point_v<T>) {
55
154
            return (std::isnan(left) && std::isnan(right)) || left == right;
56
154
        }
57
154
        return left == right;
58
154
    };
59
154
    const auto greater = [](T left, T right) {
60
154
        if constexpr (std::is_floating_point_v<T>) {
61
            // Match Doris Compare: NaN is equal to NaN and greater than every finite value.
62
154
            if (std::isnan(right)) {
63
154
                return false;
64
154
            }
65
154
            if (std::isnan(left)) {
66
154
                return true;
67
154
            }
68
154
        }
69
154
        return left > right;
70
154
    };
71
154
    switch (op) {
72
60
    case RawComparisonOp::EQ:
73
60
        return equal(lhs, rhs);
74
0
    case RawComparisonOp::NE:
75
0
        return !equal(lhs, rhs);
76
0
    case RawComparisonOp::LT:
77
0
        return greater(rhs, lhs);
78
36
    case RawComparisonOp::LE:
79
36
        return !greater(lhs, rhs);
80
8
    case RawComparisonOp::GT:
81
8
        return greater(lhs, rhs);
82
50
    case RawComparisonOp::GE:
83
50
        return !greater(rhs, lhs);
84
154
    }
85
0
    __builtin_unreachable();
86
154
}
87
88
#ifdef DORIS_PARQUET_X86_SIMD
89
template <size_t WIDTH>
90
__attribute__((target("avx2"))) void byte_stream_split_decode_avx2(const uint8_t* src,
91
                                                                   size_t offset, size_t num_values,
92
19
                                                                   size_t stride, uint8_t* dest) {
93
19
    static_assert(WIDTH == 4 || WIDTH == 8);
94
19
    constexpr size_t STEPS = WIDTH == 8 ? 3 : 2;
95
19
    constexpr size_t LANES = sizeof(__m256i);
96
19
    const size_t blocks = num_values / LANES;
97
19
    __m256i stage[STEPS + 1][WIDTH];
98
19
    __m256i result[WIDTH];
99
100
32.8k
    for (size_t block = 0; block < blocks; ++block) {
101
163k
        for (size_t stream = 0; stream < WIDTH; ++stream) {
102
131k
            stage[0][stream] = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(
103
131k
                    src + stream * stride + offset + block * LANES));
104
131k
        }
105
98.3k
        for (size_t step = 0; step < STEPS; ++step) {
106
            // AVX2 unpack instructions operate independently in each 128-bit half. Keep the
107
            // byte-transpose hierarchy lane-local, then stitch the halves only after the last
108
            // unpack so every output vector contains contiguous decoded rows.
109
196k
            for (size_t pair = 0; pair < WIDTH / 2; ++pair) {
110
131k
                stage[step + 1][pair * 2] =
111
131k
                        _mm256_unpacklo_epi8(stage[step][pair], stage[step][WIDTH / 2 + pair]);
112
131k
                stage[step + 1][pair * 2 + 1] =
113
131k
                        _mm256_unpackhi_epi8(stage[step][pair], stage[step][WIDTH / 2 + pair]);
114
131k
            }
115
65.5k
        }
116
98.3k
        for (size_t pair = 0; pair < WIDTH / 2; ++pair) {
117
65.5k
            result[pair] = _mm256_permute2x128_si256(stage[STEPS][pair * 2],
118
65.5k
                                                     stage[STEPS][pair * 2 + 1], 0x20);
119
65.5k
            result[WIDTH / 2 + pair] = _mm256_permute2x128_si256(stage[STEPS][pair * 2],
120
65.5k
                                                                 stage[STEPS][pair * 2 + 1], 0x31);
121
65.5k
        }
122
163k
        for (size_t lane = 0; lane < WIDTH; ++lane) {
123
131k
            _mm256_storeu_si256(reinterpret_cast<__m256i*>(dest + (block * WIDTH + lane) * LANES),
124
131k
                                result[lane]);
125
131k
        }
126
32.7k
    }
127
19
    const size_t processed = blocks * LANES;
128
19
    byte_stream_split_decode_scalar(src, WIDTH, offset + processed, num_values - processed, stride,
129
19
                                    dest + processed * WIDTH);
130
19
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_129byte_stream_split_decode_avx2ILm4EEEvPKhmmmPh
Line
Count
Source
92
11
                                                                   size_t stride, uint8_t* dest) {
93
11
    static_assert(WIDTH == 4 || WIDTH == 8);
94
11
    constexpr size_t STEPS = WIDTH == 8 ? 3 : 2;
95
11
    constexpr size_t LANES = sizeof(__m256i);
96
11
    const size_t blocks = num_values / LANES;
97
11
    __m256i stage[STEPS + 1][WIDTH];
98
11
    __m256i result[WIDTH];
99
100
32.7k
    for (size_t block = 0; block < blocks; ++block) {
101
163k
        for (size_t stream = 0; stream < WIDTH; ++stream) {
102
131k
            stage[0][stream] = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(
103
131k
                    src + stream * stride + offset + block * LANES));
104
131k
        }
105
98.3k
        for (size_t step = 0; step < STEPS; ++step) {
106
            // AVX2 unpack instructions operate independently in each 128-bit half. Keep the
107
            // byte-transpose hierarchy lane-local, then stitch the halves only after the last
108
            // unpack so every output vector contains contiguous decoded rows.
109
196k
            for (size_t pair = 0; pair < WIDTH / 2; ++pair) {
110
131k
                stage[step + 1][pair * 2] =
111
131k
                        _mm256_unpacklo_epi8(stage[step][pair], stage[step][WIDTH / 2 + pair]);
112
131k
                stage[step + 1][pair * 2 + 1] =
113
131k
                        _mm256_unpackhi_epi8(stage[step][pair], stage[step][WIDTH / 2 + pair]);
114
131k
            }
115
65.5k
        }
116
98.3k
        for (size_t pair = 0; pair < WIDTH / 2; ++pair) {
117
65.5k
            result[pair] = _mm256_permute2x128_si256(stage[STEPS][pair * 2],
118
65.5k
                                                     stage[STEPS][pair * 2 + 1], 0x20);
119
65.5k
            result[WIDTH / 2 + pair] = _mm256_permute2x128_si256(stage[STEPS][pair * 2],
120
65.5k
                                                                 stage[STEPS][pair * 2 + 1], 0x31);
121
65.5k
        }
122
163k
        for (size_t lane = 0; lane < WIDTH; ++lane) {
123
131k
            _mm256_storeu_si256(reinterpret_cast<__m256i*>(dest + (block * WIDTH + lane) * LANES),
124
131k
                                result[lane]);
125
131k
        }
126
32.7k
    }
127
11
    const size_t processed = blocks * LANES;
128
11
    byte_stream_split_decode_scalar(src, WIDTH, offset + processed, num_values - processed, stride,
129
11
                                    dest + processed * WIDTH);
130
11
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_129byte_stream_split_decode_avx2ILm8EEEvPKhmmmPh
Line
Count
Source
92
8
                                                                   size_t stride, uint8_t* dest) {
93
8
    static_assert(WIDTH == 4 || WIDTH == 8);
94
8
    constexpr size_t STEPS = WIDTH == 8 ? 3 : 2;
95
8
    constexpr size_t LANES = sizeof(__m256i);
96
8
    const size_t blocks = num_values / LANES;
97
8
    __m256i stage[STEPS + 1][WIDTH];
98
8
    __m256i result[WIDTH];
99
100
17
    for (size_t block = 0; block < blocks; ++block) {
101
81
        for (size_t stream = 0; stream < WIDTH; ++stream) {
102
72
            stage[0][stream] = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(
103
72
                    src + stream * stride + offset + block * LANES));
104
72
        }
105
36
        for (size_t step = 0; step < STEPS; ++step) {
106
            // AVX2 unpack instructions operate independently in each 128-bit half. Keep the
107
            // byte-transpose hierarchy lane-local, then stitch the halves only after the last
108
            // unpack so every output vector contains contiguous decoded rows.
109
135
            for (size_t pair = 0; pair < WIDTH / 2; ++pair) {
110
108
                stage[step + 1][pair * 2] =
111
108
                        _mm256_unpacklo_epi8(stage[step][pair], stage[step][WIDTH / 2 + pair]);
112
108
                stage[step + 1][pair * 2 + 1] =
113
108
                        _mm256_unpackhi_epi8(stage[step][pair], stage[step][WIDTH / 2 + pair]);
114
108
            }
115
27
        }
116
45
        for (size_t pair = 0; pair < WIDTH / 2; ++pair) {
117
36
            result[pair] = _mm256_permute2x128_si256(stage[STEPS][pair * 2],
118
36
                                                     stage[STEPS][pair * 2 + 1], 0x20);
119
36
            result[WIDTH / 2 + pair] = _mm256_permute2x128_si256(stage[STEPS][pair * 2],
120
36
                                                                 stage[STEPS][pair * 2 + 1], 0x31);
121
36
        }
122
81
        for (size_t lane = 0; lane < WIDTH; ++lane) {
123
72
            _mm256_storeu_si256(reinterpret_cast<__m256i*>(dest + (block * WIDTH + lane) * LANES),
124
72
                                result[lane]);
125
72
        }
126
9
    }
127
8
    const size_t processed = blocks * LANES;
128
8
    byte_stream_split_decode_scalar(src, WIDTH, offset + processed, num_values - processed, stride,
129
8
                                    dest + processed * WIDTH);
130
8
}
131
132
__attribute__((target("avx2"))) void delta_decode_int32_avx2(int32_t* values, size_t count,
133
                                                             int32_t min_delta,
134
16.5k
                                                             int32_t* last_value) {
135
16.5k
    const __m256i min_delta_vec = _mm256_set1_epi32(min_delta);
136
16.5k
    const size_t vector_count = count / 8 * 8;
137
212k
    for (size_t row = 0; row < vector_count; row += 8) {
138
195k
        __m256i value = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(values + row));
139
195k
        value = _mm256_add_epi32(value, min_delta_vec);
140
195k
        value = _mm256_add_epi32(value, _mm256_slli_si256(value, 4));
141
195k
        value = _mm256_add_epi32(value, _mm256_slli_si256(value, 8));
142
195k
        _mm256_storeu_si256(reinterpret_cast<__m256i*>(values + row), value);
143
195k
    }
144
16.5k
    __m128i carry = _mm_set1_epi32(*last_value);
145
407k
    for (size_t row = 0; row < vector_count; row += 4) {
146
391k
        __m128i value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(values + row));
147
391k
        value = _mm_add_epi32(value, carry);
148
391k
        _mm_storeu_si128(reinterpret_cast<__m128i*>(values + row), value);
149
391k
        carry = _mm_shuffle_epi32(value, _MM_SHUFFLE(3, 3, 3, 3));
150
391k
    }
151
16.5k
    if (vector_count != 0) {
152
16.5k
        *last_value = _mm_cvtsi128_si32(carry);
153
16.5k
    }
154
16.5k
    using Unsigned = uint32_t;
155
19.8k
    for (size_t row = vector_count; row < count; ++row) {
156
3.22k
        values[row] = static_cast<int32_t>(static_cast<Unsigned>(values[row]) +
157
3.22k
                                           static_cast<Unsigned>(min_delta) +
158
3.22k
                                           static_cast<Unsigned>(*last_value));
159
3.22k
        *last_value = values[row];
160
3.22k
    }
161
16.5k
}
162
163
__attribute__((target("avx2"))) void delta_decode_int64_avx2(int64_t* values, size_t count,
164
                                                             int64_t min_delta,
165
459
                                                             int64_t* last_value) {
166
459
    const __m256i min_delta_vec = _mm256_set1_epi64x(min_delta);
167
459
    const size_t vector_count = count / 4 * 4;
168
3.68k
    for (size_t row = 0; row < vector_count; row += 4) {
169
3.22k
        __m256i value = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(values + row));
170
3.22k
        value = _mm256_add_epi64(value, min_delta_vec);
171
3.22k
        value = _mm256_add_epi64(value, _mm256_slli_si256(value, 8));
172
3.22k
        _mm256_storeu_si256(reinterpret_cast<__m256i*>(values + row), value);
173
3.22k
    }
174
459
    __m128i carry = _mm_set1_epi64x(*last_value);
175
6.90k
    for (size_t row = 0; row < vector_count; row += 2) {
176
6.44k
        __m128i value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(values + row));
177
6.44k
        value = _mm_add_epi64(value, carry);
178
6.44k
        _mm_storeu_si128(reinterpret_cast<__m128i*>(values + row), value);
179
6.44k
        carry = _mm_unpackhi_epi64(value, value);
180
6.44k
    }
181
459
    if (vector_count != 0) {
182
459
        *last_value = _mm_cvtsi128_si64(carry);
183
459
    }
184
459
    using Unsigned = uint64_t;
185
1.83k
    for (size_t row = vector_count; row < count; ++row) {
186
1.37k
        values[row] = static_cast<int64_t>(static_cast<Unsigned>(values[row]) +
187
1.37k
                                           static_cast<Unsigned>(min_delta) +
188
1.37k
                                           static_cast<Unsigned>(*last_value));
189
1.37k
        *last_value = values[row];
190
1.37k
    }
191
459
}
192
193
__attribute__((target("avx2"))) void dictionary_gather_avx2(const uint8_t* dictionary,
194
                                                            const uint32_t* indices, size_t count,
195
4.26M
                                                            size_t value_width, uint8_t* dest) {
196
4.26M
    size_t row = 0;
197
4.26M
    if (value_width == 4) {
198
22.9M
        for (; row + 8 <= count; row += 8) {
199
21.8M
            const __m256i ids = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(indices + row));
200
21.8M
            const __m256i gathered =
201
21.8M
                    _mm256_i32gather_epi32(reinterpret_cast<const int*>(dictionary), ids, 4);
202
21.8M
            _mm256_storeu_si256(reinterpret_cast<__m256i*>(dest + row * 4), gathered);
203
21.8M
        }
204
3.07M
    } else {
205
53.8M
        for (; row + 4 <= count; row += 4) {
206
50.7M
            const __m128i ids = _mm_loadu_si128(reinterpret_cast<const __m128i*>(indices + row));
207
50.7M
            const __m256i gathered =
208
50.7M
                    _mm256_i32gather_epi64(reinterpret_cast<const long long*>(dictionary), ids, 8);
209
50.7M
            _mm256_storeu_si256(reinterpret_cast<__m256i*>(dest + row * 8), gathered);
210
50.7M
        }
211
3.07M
    }
212
10.0M
    for (; row < count; ++row) {
213
5.74M
        memcpy(dest + row * value_width, dictionary + indices[row] * value_width, value_width);
214
5.74M
    }
215
4.26M
}
216
217
template <size_t LANES>
218
0
constexpr auto make_expand_permute_lut() {
219
0
    std::array<std::array<int32_t, 8>, 1U << LANES> lut {};
220
0
    for (size_t mask = 0; mask < lut.size(); ++mask) {
221
0
        int32_t source = 0;
222
0
        for (size_t lane = 0; lane < LANES; ++lane) {
223
0
            const int32_t value = (mask & (1U << lane)) != 0 ? source++ : 0;
224
0
            if constexpr (LANES == 8) {
225
0
                lut[mask][lane] = value;
226
0
            } else {
227
0
                lut[mask][lane * 2] = value * 2;
228
0
                lut[mask][lane * 2 + 1] = value * 2 + 1;
229
0
            }
230
0
        }
231
0
    }
232
0
    return lut;
233
0
}
Unexecuted instantiation: parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_123make_expand_permute_lutILm8EEEDav
Unexecuted instantiation: parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_123make_expand_permute_lutILm4EEEDav
234
235
constexpr auto EXPAND_PERMUTE_32 = make_expand_permute_lut<8>();
236
constexpr auto EXPAND_PERMUTE_64 = make_expand_permute_lut<4>();
237
238
__attribute__((target("avx2"))) void expand_nullable_avx2(uint8_t* bytes, size_t compact_count,
239
                                                          const uint8_t* nulls, size_t output_count,
240
89
                                                          size_t value_width) {
241
89
    size_t source = compact_count;
242
89
    size_t output = output_count;
243
89
    if (value_width == 4) {
244
7
        auto* values = reinterpret_cast<int32_t*>(bytes);
245
17
        while (output >= 8) {
246
10
            const size_t start = output - 8;
247
10
            uint32_t valid_mask = 0;
248
90
            for (size_t lane = 0; lane < 8; ++lane) {
249
80
                valid_mask |= static_cast<uint32_t>(nulls[start + lane] == 0) << lane;
250
80
            }
251
10
            const size_t valid = std::popcount(valid_mask);
252
10
            source -= valid;
253
10
            const __m256i load_mask = _mm256_cmpgt_epi32(_mm256_set1_epi32(static_cast<int>(valid)),
254
10
                                                         _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7));
255
10
            const __m256i compact = _mm256_maskload_epi32(values + source, load_mask);
256
10
            const __m256i permute = _mm256_loadu_si256(
257
10
                    reinterpret_cast<const __m256i*>(EXPAND_PERMUTE_32[valid_mask].data()));
258
10
            __m256i expanded = _mm256_permutevar8x32_epi32(compact, permute);
259
10
            const __m256i valid_lanes = _mm256_setr_epi32(
260
10
                    -(valid_mask & 1U), -((valid_mask >> 1) & 1U), -((valid_mask >> 2) & 1U),
261
10
                    -((valid_mask >> 3) & 1U), -((valid_mask >> 4) & 1U), -((valid_mask >> 5) & 1U),
262
10
                    -((valid_mask >> 6) & 1U), -((valid_mask >> 7) & 1U));
263
10
            expanded = _mm256_and_si256(expanded, valid_lanes);
264
10
            _mm256_storeu_si256(reinterpret_cast<__m256i*>(values + start), expanded);
265
10
            output = start;
266
10
        }
267
82
    } else {
268
82
        auto* values = reinterpret_cast<int64_t*>(bytes);
269
601
        while (output >= 4) {
270
519
            const size_t start = output - 4;
271
519
            uint32_t valid_mask = 0;
272
2.59k
            for (size_t lane = 0; lane < 4; ++lane) {
273
2.07k
                valid_mask |= static_cast<uint32_t>(nulls[start + lane] == 0) << lane;
274
2.07k
            }
275
519
            const size_t valid = std::popcount(valid_mask);
276
519
            source -= valid;
277
519
            const __m256i load_mask =
278
519
                    _mm256_setr_epi64x(valid > 0 ? -1LL : 0, valid > 1 ? -1LL : 0,
279
519
                                       valid > 2 ? -1LL : 0, valid > 3 ? -1LL : 0);
280
519
            const __m256i compact = _mm256_maskload_epi64(
281
519
                    reinterpret_cast<const long long*>(values + source), load_mask);
282
519
            const __m256i permute = _mm256_loadu_si256(
283
519
                    reinterpret_cast<const __m256i*>(EXPAND_PERMUTE_64[valid_mask].data()));
284
519
            __m256i expanded = _mm256_permutevar8x32_epi32(compact, permute);
285
519
            const __m256i valid_lanes = _mm256_setr_epi64x(
286
519
                    (valid_mask & 1U) != 0 ? -1LL : 0, (valid_mask & 2U) != 0 ? -1LL : 0,
287
519
                    (valid_mask & 4U) != 0 ? -1LL : 0, (valid_mask & 8U) != 0 ? -1LL : 0);
288
519
            expanded = _mm256_and_si256(expanded, valid_lanes);
289
519
            _mm256_storeu_si256(reinterpret_cast<__m256i*>(values + start), expanded);
290
519
            output = start;
291
519
        }
292
82
    }
293
175
    while (output > 0) {
294
86
        --output;
295
86
        if (nulls[output] != 0) {
296
7
            memset(bytes + output * value_width, 0, value_width);
297
79
        } else {
298
79
            --source;
299
79
            memmove(bytes + output * value_width, bytes + source * value_width, value_width);
300
79
        }
301
86
    }
302
89
}
303
304
9.38M
__attribute__((target("avx2"))) __m256i vector_all(__m256i) {
305
9.38M
    return _mm256_set1_epi32(-1);
306
9.38M
}
307
308
5
__attribute__((target("avx2"))) __m256 vector_all(__m256) {
309
5
    return _mm256_castsi256_ps(_mm256_set1_epi32(-1));
310
5
}
311
312
1.21k
__attribute__((target("avx2"))) __m256d vector_all(__m256d) {
313
1.21k
    return _mm256_castsi256_pd(_mm256_set1_epi32(-1));
314
1.21k
}
315
316
9.26M
__attribute__((target("avx2"))) __m256i vector_or(__m256i lhs, __m256i rhs) {
317
9.26M
    return _mm256_or_si256(lhs, rhs);
318
9.26M
}
319
320
1
__attribute__((target("avx2"))) __m256 vector_or(__m256 lhs, __m256 rhs) {
321
1
    return _mm256_or_ps(lhs, rhs);
322
1
}
323
324
6
__attribute__((target("avx2"))) __m256d vector_or(__m256d lhs, __m256d rhs) {
325
6
    return _mm256_or_pd(lhs, rhs);
326
6
}
327
328
5
__attribute__((target("avx2"))) __m256i vector_xor(__m256i lhs, __m256i rhs) {
329
5
    return _mm256_xor_si256(lhs, rhs);
330
5
}
331
332
0
__attribute__((target("avx2"))) __m256 vector_xor(__m256 lhs, __m256 rhs) {
333
0
    return _mm256_xor_ps(lhs, rhs);
334
0
}
335
336
2
__attribute__((target("avx2"))) __m256d vector_xor(__m256d lhs, __m256d rhs) {
337
2
    return _mm256_xor_pd(lhs, rhs);
338
2
}
339
340
template <typename Vec>
341
__attribute__((target("avx2"))) Vec combine_comparison(Vec equal, Vec greater, Vec less,
342
9.38M
                                                       RawComparisonOp op) {
343
    // Target features do not propagate from AVX2 callers into separately instantiated helpers.
344
    // Keep the complete vector operation inside its own target scope for baseline x86 builds.
345
9.38M
    const Vec all = vector_all(equal);
346
9.38M
    switch (op) {
347
17.1k
    case RawComparisonOp::EQ:
348
17.1k
        return equal;
349
7
    case RawComparisonOp::NE:
350
7
        return vector_xor(equal, all);
351
42.6k
    case RawComparisonOp::LT:
352
42.6k
        return less;
353
4.64M
    case RawComparisonOp::LE:
354
4.64M
        return vector_or(less, equal);
355
55.8k
    case RawComparisonOp::GT:
356
55.8k
        return greater;
357
4.64M
    case RawComparisonOp::GE:
358
4.64M
        return vector_or(greater, equal);
359
9.38M
    }
360
0
    __builtin_unreachable();
361
9.38M
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_118combine_comparisonIDv4_xEET_S4_S4_S4_NS0_15RawComparisonOpE
Line
Count
Source
342
9.38M
                                                       RawComparisonOp op) {
343
    // Target features do not propagate from AVX2 callers into separately instantiated helpers.
344
    // Keep the complete vector operation inside its own target scope for baseline x86 builds.
345
9.38M
    const Vec all = vector_all(equal);
346
9.38M
    switch (op) {
347
17.1k
    case RawComparisonOp::EQ:
348
17.1k
        return equal;
349
5
    case RawComparisonOp::NE:
350
5
        return vector_xor(equal, all);
351
42.4k
    case RawComparisonOp::LT:
352
42.4k
        return less;
353
4.64M
    case RawComparisonOp::LE:
354
4.64M
        return vector_or(less, equal);
355
54.8k
    case RawComparisonOp::GT:
356
54.8k
        return greater;
357
4.64M
    case RawComparisonOp::GE:
358
4.64M
        return vector_or(greater, equal);
359
9.38M
    }
360
0
    __builtin_unreachable();
361
9.38M
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_118combine_comparisonIDv8_fEET_S4_S4_S4_NS0_15RawComparisonOpE
Line
Count
Source
342
5
                                                       RawComparisonOp op) {
343
    // Target features do not propagate from AVX2 callers into separately instantiated helpers.
344
    // Keep the complete vector operation inside its own target scope for baseline x86 builds.
345
5
    const Vec all = vector_all(equal);
346
5
    switch (op) {
347
3
    case RawComparisonOp::EQ:
348
3
        return equal;
349
0
    case RawComparisonOp::NE:
350
0
        return vector_xor(equal, all);
351
0
    case RawComparisonOp::LT:
352
0
        return less;
353
1
    case RawComparisonOp::LE:
354
1
        return vector_or(less, equal);
355
1
    case RawComparisonOp::GT:
356
1
        return greater;
357
0
    case RawComparisonOp::GE:
358
0
        return vector_or(greater, equal);
359
5
    }
360
0
    __builtin_unreachable();
361
5
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_118combine_comparisonIDv4_dEET_S4_S4_S4_NS0_15RawComparisonOpE
Line
Count
Source
342
1.21k
                                                       RawComparisonOp op) {
343
    // Target features do not propagate from AVX2 callers into separately instantiated helpers.
344
    // Keep the complete vector operation inside its own target scope for baseline x86 builds.
345
1.21k
    const Vec all = vector_all(equal);
346
1.21k
    switch (op) {
347
8
    case RawComparisonOp::EQ:
348
8
        return equal;
349
2
    case RawComparisonOp::NE:
350
2
        return vector_xor(equal, all);
351
198
    case RawComparisonOp::LT:
352
198
        return less;
353
4
    case RawComparisonOp::LE:
354
4
        return vector_or(less, equal);
355
1.00k
    case RawComparisonOp::GT:
356
1.00k
        return greater;
357
2
    case RawComparisonOp::GE:
358
2
        return vector_or(greater, equal);
359
1.21k
    }
360
0
    __builtin_unreachable();
361
1.21k
}
362
363
__attribute__((target("avx2"))) void raw_compare_int32_avx2(const uint8_t* bytes, size_t count,
364
                                                            int32_t literal, RawComparisonOp op,
365
13.6k
                                                            uint8_t* matches) {
366
13.6k
    size_t row = 0;
367
13.6k
    const __m256i rhs = _mm256_set1_epi32(literal);
368
9.39M
    for (; row + 8 <= count; row += 8) {
369
9.38M
        const __m256i lhs = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(bytes + row * 4));
370
9.38M
        const __m256i equal = _mm256_cmpeq_epi32(lhs, rhs);
371
9.38M
        const __m256i greater = _mm256_cmpgt_epi32(lhs, rhs);
372
9.38M
        const __m256i less = _mm256_cmpgt_epi32(rhs, lhs);
373
9.38M
        const uint32_t mask = static_cast<uint32_t>(_mm256_movemask_ps(
374
9.38M
                _mm256_castsi256_ps(combine_comparison(equal, greater, less, op))));
375
84.4M
        for (size_t lane = 0; lane < 8; ++lane) {
376
75.0M
            matches[row + lane] &= static_cast<uint8_t>((mask >> lane) & 1U);
377
75.0M
        }
378
9.38M
    }
379
17.6k
    for (; row < count; ++row) {
380
4.00k
        int32_t value;
381
4.00k
        memcpy(&value, bytes + row * 4, sizeof(value));
382
4.00k
        bool keep = false;
383
4.00k
        switch (op) {
384
370
        case RawComparisonOp::EQ:
385
370
            keep = value == literal;
386
370
            break;
387
31
        case RawComparisonOp::NE:
388
31
            keep = value != literal;
389
31
            break;
390
26
        case RawComparisonOp::LT:
391
26
            keep = value < literal;
392
26
            break;
393
254
        case RawComparisonOp::LE:
394
254
            keep = value <= literal;
395
254
            break;
396
162
        case RawComparisonOp::GT:
397
162
            keep = value > literal;
398
162
            break;
399
3.15k
        case RawComparisonOp::GE:
400
3.15k
            keep = value >= literal;
401
3.15k
            break;
402
4.00k
        }
403
4.00k
        matches[row] &= static_cast<uint8_t>(keep);
404
4.00k
    }
405
13.6k
}
406
407
__attribute__((target("avx2"))) void raw_compare_int64_avx2(const uint8_t* bytes, size_t count,
408
                                                            int64_t literal, RawComparisonOp op,
409
69
                                                            uint8_t* matches) {
410
69
    size_t row = 0;
411
69
    const __m256i rhs = _mm256_set1_epi64x(literal);
412
1.77k
    for (; row + 4 <= count; row += 4) {
413
1.70k
        const __m256i lhs = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(bytes + row * 8));
414
1.70k
        const __m256i equal = _mm256_cmpeq_epi64(lhs, rhs);
415
1.70k
        const __m256i greater = _mm256_cmpgt_epi64(lhs, rhs);
416
1.70k
        const __m256i less = _mm256_cmpgt_epi64(rhs, lhs);
417
1.70k
        const uint32_t mask = static_cast<uint32_t>(_mm256_movemask_pd(
418
1.70k
                _mm256_castsi256_pd(combine_comparison(equal, greater, less, op))));
419
8.53k
        for (size_t lane = 0; lane < 4; ++lane) {
420
6.82k
            matches[row + lane] &= static_cast<uint8_t>((mask >> lane) & 1U);
421
6.82k
        }
422
1.70k
    }
423
105
    for (; row < count; ++row) {
424
36
        int64_t value;
425
36
        memcpy(&value, bytes + row * 8, sizeof(value));
426
36
        bool keep = false;
427
36
        switch (op) {
428
26
        case RawComparisonOp::EQ:
429
26
            keep = value == literal;
430
26
            break;
431
0
        case RawComparisonOp::NE:
432
0
            keep = value != literal;
433
0
            break;
434
1
        case RawComparisonOp::LT:
435
1
            keep = value < literal;
436
1
            break;
437
0
        case RawComparisonOp::LE:
438
0
            keep = value <= literal;
439
0
            break;
440
2
        case RawComparisonOp::GT:
441
2
            keep = value > literal;
442
2
            break;
443
7
        case RawComparisonOp::GE:
444
7
            keep = value >= literal;
445
7
            break;
446
36
        }
447
36
        matches[row] &= static_cast<uint8_t>(keep);
448
36
    }
449
69
}
450
451
__attribute__((target("avx2"))) void raw_compare_float_avx2(const uint8_t* bytes, size_t count,
452
                                                            float literal, RawComparisonOp op,
453
5
                                                            uint8_t* matches) {
454
5
    size_t row = 0;
455
5
    const __m256 rhs = _mm256_set1_ps(literal);
456
10
    for (; row + 8 <= count; row += 8) {
457
5
        const __m256 lhs = _mm256_loadu_ps(reinterpret_cast<const float*>(bytes + row * 4));
458
5
        const __m256 lhs_nan = _mm256_cmp_ps(lhs, lhs, _CMP_UNORD_Q);
459
5
        const __m256 rhs_nan = _mm256_cmp_ps(rhs, rhs, _CMP_UNORD_Q);
460
5
        const __m256 both_nan = _mm256_and_ps(lhs_nan, rhs_nan);
461
5
        const __m256 equal = _mm256_or_ps(_mm256_cmp_ps(lhs, rhs, _CMP_EQ_OQ), both_nan);
462
5
        const __m256 greater = _mm256_or_ps(_mm256_cmp_ps(lhs, rhs, _CMP_GT_OQ),
463
5
                                            _mm256_andnot_ps(rhs_nan, lhs_nan));
464
5
        const __m256 less = _mm256_or_ps(_mm256_cmp_ps(lhs, rhs, _CMP_LT_OQ),
465
5
                                         _mm256_andnot_ps(lhs_nan, rhs_nan));
466
5
        const uint32_t mask = static_cast<uint32_t>(
467
5
                _mm256_movemask_ps(combine_comparison(equal, greater, less, op)));
468
45
        for (size_t lane = 0; lane < 8; ++lane) {
469
40
            matches[row + lane] &= static_cast<uint8_t>((mask >> lane) & 1U);
470
40
        }
471
5
    }
472
18
    for (; row < count; ++row) {
473
13
        float value;
474
13
        memcpy(&value, bytes + row * 4, sizeof(value));
475
13
        matches[row] &= static_cast<uint8_t>(scalar_compare(value, literal, op));
476
13
    }
477
5
}
478
479
__attribute__((target("avx2"))) void raw_compare_double_avx2(const uint8_t* bytes, size_t count,
480
                                                             double literal, RawComparisonOp op,
481
59
                                                             uint8_t* matches) {
482
59
    size_t row = 0;
483
59
    const __m256d rhs = _mm256_set1_pd(literal);
484
1.27k
    for (; row + 4 <= count; row += 4) {
485
1.21k
        const __m256d lhs = _mm256_loadu_pd(reinterpret_cast<const double*>(bytes + row * 8));
486
1.21k
        const __m256d lhs_nan = _mm256_cmp_pd(lhs, lhs, _CMP_UNORD_Q);
487
1.21k
        const __m256d rhs_nan = _mm256_cmp_pd(rhs, rhs, _CMP_UNORD_Q);
488
1.21k
        const __m256d both_nan = _mm256_and_pd(lhs_nan, rhs_nan);
489
1.21k
        const __m256d equal = _mm256_or_pd(_mm256_cmp_pd(lhs, rhs, _CMP_EQ_OQ), both_nan);
490
1.21k
        const __m256d greater = _mm256_or_pd(_mm256_cmp_pd(lhs, rhs, _CMP_GT_OQ),
491
1.21k
                                             _mm256_andnot_pd(rhs_nan, lhs_nan));
492
1.21k
        const __m256d less = _mm256_or_pd(_mm256_cmp_pd(lhs, rhs, _CMP_LT_OQ),
493
1.21k
                                          _mm256_andnot_pd(lhs_nan, rhs_nan));
494
1.21k
        const uint32_t mask = static_cast<uint32_t>(
495
1.21k
                _mm256_movemask_pd(combine_comparison(equal, greater, less, op)));
496
6.08k
        for (size_t lane = 0; lane < 4; ++lane) {
497
4.86k
            matches[row + lane] &= static_cast<uint8_t>((mask >> lane) & 1U);
498
4.86k
        }
499
1.21k
    }
500
63
    for (; row < count; ++row) {
501
4
        double value;
502
4
        memcpy(&value, bytes + row * 8, sizeof(value));
503
4
        matches[row] &= static_cast<uint8_t>(scalar_compare(value, literal, op));
504
4
    }
505
59
}
506
#endif
507
508
template <typename T>
509
330
void delta_decode_scalar(T* values, size_t count, T min_delta, T* last_value) {
510
330
    using Unsigned = std::make_unsigned_t<T>;
511
1.21k
    for (size_t row = 0; row < count; ++row) {
512
889
        values[row] = static_cast<T>(static_cast<Unsigned>(values[row]) +
513
889
                                     static_cast<Unsigned>(min_delta) +
514
889
                                     static_cast<Unsigned>(*last_value));
515
889
        *last_value = values[row];
516
889
    }
517
330
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_119delta_decode_scalarIiEEvPT_mS3_S4_
Line
Count
Source
509
330
void delta_decode_scalar(T* values, size_t count, T min_delta, T* last_value) {
510
330
    using Unsigned = std::make_unsigned_t<T>;
511
1.21k
    for (size_t row = 0; row < count; ++row) {
512
889
        values[row] = static_cast<T>(static_cast<Unsigned>(values[row]) +
513
889
                                     static_cast<Unsigned>(min_delta) +
514
889
                                     static_cast<Unsigned>(*last_value));
515
889
        *last_value = values[row];
516
889
    }
517
330
}
Unexecuted instantiation: parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_119delta_decode_scalarIlEEvPT_mS3_S4_
518
519
template <typename T>
520
void raw_compare_scalar(const uint8_t* bytes, size_t count, T literal, RawComparisonOp op,
521
1.23k
                        uint8_t* matches) {
522
3.28k
    for (size_t row = 0; row < count; ++row) {
523
2.04k
        if (matches[row] == 0) {
524
11
            continue;
525
11
        }
526
2.03k
        T value;
527
2.03k
        memcpy(&value, bytes + row * sizeof(T), sizeof(T));
528
2.03k
        matches[row] = static_cast<uint8_t>(scalar_compare(value, literal, op));
529
2.03k
    }
530
1.23k
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_118raw_compare_scalarIiEEvPKhmT_NS0_15RawComparisonOpEPh
Line
Count
Source
521
1.08k
                        uint8_t* matches) {
522
2.86k
    for (size_t row = 0; row < count; ++row) {
523
1.78k
        if (matches[row] == 0) {
524
11
            continue;
525
11
        }
526
1.77k
        T value;
527
1.77k
        memcpy(&value, bytes + row * sizeof(T), sizeof(T));
528
1.77k
        matches[row] = static_cast<uint8_t>(scalar_compare(value, literal, op));
529
1.77k
    }
530
1.08k
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_118raw_compare_scalarIlEEvPKhmT_NS0_15RawComparisonOpEPh
Line
Count
Source
521
112
                        uint8_t* matches) {
522
266
    for (size_t row = 0; row < count; ++row) {
523
154
        if (matches[row] == 0) {
524
0
            continue;
525
0
        }
526
154
        T value;
527
154
        memcpy(&value, bytes + row * sizeof(T), sizeof(T));
528
154
        matches[row] = static_cast<uint8_t>(scalar_compare(value, literal, op));
529
154
    }
530
112
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_118raw_compare_scalarIfEEvPKhmT_NS0_15RawComparisonOpEPh
Line
Count
Source
521
6
                        uint8_t* matches) {
522
22
    for (size_t row = 0; row < count; ++row) {
523
16
        if (matches[row] == 0) {
524
0
            continue;
525
0
        }
526
16
        T value;
527
16
        memcpy(&value, bytes + row * sizeof(T), sizeof(T));
528
16
        matches[row] = static_cast<uint8_t>(scalar_compare(value, literal, op));
529
16
    }
530
6
}
parquet_kernels.cpp:_ZN5doris4simd12_GLOBAL__N_118raw_compare_scalarIdEEvPKhmT_NS0_15RawComparisonOpEPh
Line
Count
Source
521
34
                        uint8_t* matches) {
522
128
    for (size_t row = 0; row < count; ++row) {
523
94
        if (matches[row] == 0) {
524
0
            continue;
525
0
        }
526
94
        T value;
527
94
        memcpy(&value, bytes + row * sizeof(T), sizeof(T));
528
94
        matches[row] = static_cast<uint8_t>(scalar_compare(value, literal, op));
529
94
    }
530
34
}
531
532
} // namespace
533
534
bool try_byte_stream_split_decode(const uint8_t* src, size_t width, size_t offset,
535
39
                                  size_t num_values, size_t stride, uint8_t* dest) {
536
39
#ifdef DORIS_PARQUET_X86_SIMD
537
39
    if (has_avx2() && num_values >= 32 && (width == 4 || width == 8)) {
538
19
        if (width == 4) {
539
11
            byte_stream_split_decode_avx2<4>(src, offset, num_values, stride, dest);
540
11
        } else {
541
8
            byte_stream_split_decode_avx2<8>(src, offset, num_values, stride, dest);
542
8
        }
543
19
        return true;
544
19
    }
545
20
#endif
546
20
    return false;
547
39
}
548
549
16.9k
void delta_decode(int32_t* values, size_t count, int32_t min_delta, int32_t* last_value) {
550
16.9k
#ifdef DORIS_PARQUET_X86_SIMD
551
16.9k
    if (has_avx2() && count >= 8) {
552
16.5k
        delta_decode_int32_avx2(values, count, min_delta, last_value);
553
16.5k
        return;
554
16.5k
    }
555
330
#endif
556
330
    delta_decode_scalar(values, count, min_delta, last_value);
557
330
}
558
559
459
void delta_decode(int64_t* values, size_t count, int64_t min_delta, int64_t* last_value) {
560
459
#ifdef DORIS_PARQUET_X86_SIMD
561
459
    if (has_avx2() && count >= 4) {
562
459
        delta_decode_int64_avx2(values, count, min_delta, last_value);
563
459
        return;
564
459
    }
565
0
#endif
566
0
    delta_decode_scalar(values, count, min_delta, last_value);
567
0
}
568
569
void dictionary_gather(const uint8_t* dictionary, const uint32_t* indices, size_t count,
570
4.26M
                       size_t value_width, uint8_t* dest) {
571
4.26M
#ifdef DORIS_PARQUET_X86_SIMD
572
4.26M
    if (has_avx2() && ((value_width == 4 && count >= 8) || (value_width == 8 && count >= 4))) {
573
4.26M
        dictionary_gather_avx2(dictionary, indices, count, value_width, dest);
574
4.26M
        return;
575
4.26M
    }
576
390
#endif
577
390
    for (size_t row = 0; row < count; ++row) {
578
0
        memcpy(dest + row * value_width, dictionary + indices[row] * value_width, value_width);
579
0
    }
580
390
}
581
582
void expand_nullable_values(uint8_t* values, size_t compact_count, const uint8_t* nulls,
583
627
                            size_t output_count, size_t value_width) {
584
627
#ifdef DORIS_PARQUET_X86_SIMD
585
627
    if (has_avx2() &&
586
627
        ((value_width == 4 && output_count >= 8) || (value_width == 8 && output_count >= 4))) {
587
        // Backward expansion is required because the compact input and expanded output alias.
588
        // Each SIMD block loads all of its source lanes before overwriting the wider destination.
589
89
        expand_nullable_avx2(values, compact_count, nulls, output_count, value_width);
590
89
        return;
591
89
    }
592
538
#endif
593
538
    size_t source = compact_count;
594
1.53k
    for (size_t output = output_count; output > 0;) {
595
1.00k
        --output;
596
1.00k
        if (nulls[output] != 0) {
597
279
            memset(values + output * value_width, 0, value_width);
598
722
        } else {
599
722
            --source;
600
722
            memmove(values + output * value_width, values + source * value_width, value_width);
601
722
        }
602
1.00k
    }
603
538
}
604
605
void raw_compare(const uint8_t* values, size_t count, int32_t literal, RawComparisonOp op,
606
14.7k
                 uint8_t* matches) {
607
14.7k
#ifdef DORIS_PARQUET_X86_SIMD
608
14.7k
    if (has_avx2() && count >= 8) {
609
13.6k
        raw_compare_int32_avx2(values, count, literal, op, matches);
610
13.6k
        return;
611
13.6k
    }
612
1.08k
#endif
613
1.08k
    raw_compare_scalar(values, count, literal, op, matches);
614
1.08k
}
615
616
void raw_compare(const uint8_t* values, size_t count, int64_t literal, RawComparisonOp op,
617
181
                 uint8_t* matches) {
618
181
#ifdef DORIS_PARQUET_X86_SIMD
619
181
    if (has_avx2() && count >= 4) {
620
69
        raw_compare_int64_avx2(values, count, literal, op, matches);
621
69
        return;
622
69
    }
623
112
#endif
624
112
    raw_compare_scalar(values, count, literal, op, matches);
625
112
}
626
627
void raw_compare(const uint8_t* values, size_t count, float literal, RawComparisonOp op,
628
11
                 uint8_t* matches) {
629
11
#ifdef DORIS_PARQUET_X86_SIMD
630
11
    if (has_avx2() && count >= 8) {
631
        // Doris orders NaN above every finite value and considers NaN equal to NaN. The SIMD
632
        // masks deliberately reconstruct that total order instead of using ordered FP compares.
633
5
        raw_compare_float_avx2(values, count, literal, op, matches);
634
5
        return;
635
5
    }
636
6
#endif
637
6
    raw_compare_scalar(values, count, literal, op, matches);
638
6
}
639
640
void raw_compare(const uint8_t* values, size_t count, double literal, RawComparisonOp op,
641
93
                 uint8_t* matches) {
642
93
#ifdef DORIS_PARQUET_X86_SIMD
643
93
    if (has_avx2() && count >= 4) {
644
59
        raw_compare_double_avx2(values, count, literal, op, matches);
645
59
        return;
646
59
    }
647
34
#endif
648
34
    raw_compare_scalar(values, count, literal, op, matches);
649
34
}
650
651
} // namespace doris::simd