diff --git a/cpp/src/parquet/CMakeLists.txt b/cpp/src/parquet/CMakeLists.txt index 606dcdc0a9e3..4a7c7570b573 100644 --- a/cpp/src/parquet/CMakeLists.txt +++ b/cpp/src/parquet/CMakeLists.txt @@ -193,6 +193,15 @@ set(PARQUET_SRCS stream_writer.cc types.cc) +# guards itself: Neon on aarch64, SSE4.2 on x86, empty elsewhere +list(APPEND PARQUET_SRCS level_comparison_simd_128.cc) +if(ARROW_CPU_FLAG STREQUAL "x86" AND ARROW_HAVE_RUNTIME_SSE4_2) + set_source_files_properties(level_comparison_simd_128.cc + PROPERTIES COMPILE_OPTIONS "${ARROW_SSE4_2_FLAG}") +endif() + +append_runtime_avx2_src(PARQUET_SRCS level_comparison_simd_256.cc) + if(ARROW_HAVE_RUNTIME_AVX2) # AVX2 is used as a proxy for BMI2. list(APPEND diff --git a/cpp/src/parquet/level_comparison.cc b/cpp/src/parquet/level_comparison.cc index 81e424458537..beb9c66c5f91 100644 --- a/cpp/src/parquet/level_comparison.cc +++ b/cpp/src/parquet/level_comparison.cc @@ -23,6 +23,8 @@ # include "parquet/level_comparison_avx2_internal.h" #endif +#include "parquet/level_comparison_simd_internal.h" + #define PARQUET_IMPL_NAMESPACE standard #include "parquet/level_comparison_inc.h" #undef PARQUET_IMPL_NAMESPACE @@ -37,8 +39,6 @@ using ::arrow::internal::DispatchLevel; using ::arrow::internal::DynamicDispatch; using ::arrow::internal::DynamicDispatchTarget; -// defined in level_comparison_avx2.cc - struct GreaterThanDynamicFunction { using FunctionType = decltype(&GreaterThanBitmap); @@ -56,6 +56,8 @@ struct MinMaxDynamicFunction { static constexpr auto targets() { return std::array{ ARROW_DISPATCH_TARGET_NONE(&standard::FindMinMaxImpl) // + ARROW_DISPATCH_TARGET_NEON(&FindMinMaxNeon) // + ARROW_DISPATCH_TARGET_SSE4_2(&FindMinMaxSse42) // ARROW_DISPATCH_TARGET_AVX2(&FindMinMaxAvx2) // }; } diff --git a/cpp/src/parquet/level_comparison.h b/cpp/src/parquet/level_comparison.h index 3ae442dd46e5..966b21fad377 100644 --- a/cpp/src/parquet/level_comparison.h +++ b/cpp/src/parquet/level_comparison.h @@ -33,6 +33,6 @@ struct MinMax { int16_t max; }; -MinMax FindMinMax(const int16_t* levels, int64_t num_levels); +MinMax PARQUET_EXPORT FindMinMax(const int16_t* levels, int64_t num_levels); } // namespace parquet::internal diff --git a/cpp/src/parquet/level_comparison_avx2.cc b/cpp/src/parquet/level_comparison_avx2.cc index db8935d4037b..12cb122a9429 100644 --- a/cpp/src/parquet/level_comparison_avx2.cc +++ b/cpp/src/parquet/level_comparison_avx2.cc @@ -28,9 +28,5 @@ uint64_t GreaterThanBitmapAvx2(const int16_t* levels, int64_t num_levels, int16_ return avx2::GreaterThanBitmapImpl(levels, num_levels, rhs); } -MinMax FindMinMaxAvx2(const int16_t* levels, int64_t num_levels) { - return avx2::FindMinMaxImpl(levels, num_levels); -} - } // namespace internal } // namespace parquet diff --git a/cpp/src/parquet/level_comparison_avx2_internal.h b/cpp/src/parquet/level_comparison_avx2_internal.h index 3f68a6ff05d5..d95fb8564199 100644 --- a/cpp/src/parquet/level_comparison_avx2_internal.h +++ b/cpp/src/parquet/level_comparison_avx2_internal.h @@ -23,7 +23,6 @@ namespace parquet::internal { -MinMax FindMinMaxAvx2(const int16_t* levels, int64_t num_levels); uint64_t GreaterThanBitmapAvx2(const int16_t* levels, int64_t num_levels, int16_t rhs); } // namespace parquet::internal diff --git a/cpp/src/parquet/level_comparison_simd_128.cc b/cpp/src/parquet/level_comparison_simd_128.cc new file mode 100644 index 000000000000..af5f5f882ad9 --- /dev/null +++ b/cpp/src/parquet/level_comparison_simd_128.cc @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#if defined(ARROW_HAVE_NEON) +# define FIND_MIN_MAX_PLATFORM FindMinMaxNeon +# define ARCH_PLATFORM xsimd::neon64 +#elif defined(ARROW_HAVE_SSE4_2) || defined(ARROW_HAVE_RUNTIME_SSE4_2) +# define FIND_MIN_MAX_PLATFORM FindMinMaxSse42 +# define ARCH_PLATFORM xsimd::sse4_2 +#endif + +#if defined(FIND_MIN_MAX_PLATFORM) + +# include "parquet/level_comparison_simd_internal.h" +# include "parquet/level_comparison_simd_kernel_internal.h" + +namespace parquet::internal { + +MinMax FIND_MIN_MAX_PLATFORM(const int16_t* levels, int64_t num_levels) { + return FindMinMaxSimd(levels, num_levels); +} + +} // namespace parquet::internal + +# undef ARCH_PLATFORM +# undef FIND_MIN_MAX_PLATFORM +#endif // FIND_MIN_MAX_PLATFORM diff --git a/cpp/src/parquet/level_comparison_simd_256.cc b/cpp/src/parquet/level_comparison_simd_256.cc new file mode 100644 index 000000000000..024353dbc855 --- /dev/null +++ b/cpp/src/parquet/level_comparison_simd_256.cc @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#if defined(ARROW_HAVE_RUNTIME_AVX2) +# define FIND_MIN_MAX_PLATFORM FindMinMaxAvx2 +# define ARCH_PLATFORM xsimd::avx2 +#endif + +#if !defined(FIND_MIN_MAX_PLATFORM) +# error "This file must be compiled with a known SIMD micro architecture" +#endif + +#include "parquet/level_comparison_simd_internal.h" +#include "parquet/level_comparison_simd_kernel_internal.h" + +namespace parquet::internal { + +MinMax FIND_MIN_MAX_PLATFORM(const int16_t* levels, int64_t num_levels) { + return FindMinMaxSimd(levels, num_levels); +} + +} // namespace parquet::internal + +#undef ARCH_PLATFORM +#undef FIND_MIN_MAX_PLATFORM diff --git a/cpp/src/parquet/level_comparison_simd_internal.h b/cpp/src/parquet/level_comparison_simd_internal.h new file mode 100644 index 000000000000..518ce16f39e8 --- /dev/null +++ b/cpp/src/parquet/level_comparison_simd_internal.h @@ -0,0 +1,38 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include + +#include "parquet/level_comparison.h" + +namespace parquet::internal { + +#if defined(ARROW_HAVE_NEON) +MinMax FindMinMaxNeon(const int16_t* levels, int64_t num_levels); +#endif + +#if defined(ARROW_HAVE_SSE4_2) || defined(ARROW_HAVE_RUNTIME_SSE4_2) +MinMax FindMinMaxSse42(const int16_t* levels, int64_t num_levels); +#endif + +#if defined(ARROW_HAVE_RUNTIME_AVX2) +MinMax FindMinMaxAvx2(const int16_t* levels, int64_t num_levels); +#endif + +} // namespace parquet::internal diff --git a/cpp/src/parquet/level_comparison_simd_kernel_internal.h b/cpp/src/parquet/level_comparison_simd_kernel_internal.h new file mode 100644 index 000000000000..5b15a8f77b01 --- /dev/null +++ b/cpp/src/parquet/level_comparison_simd_kernel_internal.h @@ -0,0 +1,56 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include + +#include +#include +#include + +#include "parquet/level_comparison.h" + +namespace parquet::internal { + +template +MinMax FindMinMaxSimd(const int16_t* levels, int64_t num_levels) { + using batch = xsimd::batch; + constexpr int64_t kLanes = static_cast(batch::size); + + MinMax out{std::numeric_limits::max(), std::numeric_limits::min()}; + + int64_t i = 0; + if (num_levels >= kLanes) { + batch vmin(out.min); + batch vmax(out.max); + for (; i + kLanes <= num_levels; i += kLanes) { + const auto v = batch::load_unaligned(levels + i); + vmin = xsimd::min(vmin, v); + vmax = xsimd::max(vmax, v); + } + out.min = xsimd::reduce_min(vmin); + out.max = xsimd::reduce_max(vmax); + } + for (; i < num_levels; ++i) { + out.min = std::min(levels[i], out.min); + out.max = std::max(levels[i], out.max); + } + return out; +} + +} // namespace parquet::internal diff --git a/cpp/src/parquet/level_conversion_benchmark.cc b/cpp/src/parquet/level_conversion_benchmark.cc index f3a4f8095e3a..0702b39ec20d 100644 --- a/cpp/src/parquet/level_conversion_benchmark.cc +++ b/cpp/src/parquet/level_conversion_benchmark.cc @@ -19,6 +19,7 @@ #include #include "benchmark/benchmark.h" +#include "parquet/level_comparison.h" #include "parquet/level_conversion.h" constexpr int64_t kLevelCount = 2048; @@ -78,3 +79,22 @@ void BM_DefinitionLevelsToBitmapRepeatedMostPresent(::benchmark::State& state) { } BENCHMARK(BM_DefinitionLevelsToBitmapRepeatedMostPresent); + +void BM_FindMinMax(::benchmark::State& state) { + const int64_t num_levels = state.range(0); + std::vector levels(/*count=*/static_cast(num_levels), + kPresentDefLevel); + for (size_t x = 0; x < levels.size(); x++) { + if (x % 10 == 0) { + levels[x] = kMissingDefLevel; + } + } + for (auto _ : state) { + auto min_max = parquet::internal::FindMinMax(levels.data(), num_levels); + ::benchmark::DoNotOptimize(min_max); + } + state.SetItemsProcessed(int64_t(state.iterations()) * num_levels); + state.SetBytesProcessed(int64_t(state.iterations()) * num_levels * sizeof(int16_t)); +} + +BENCHMARK(BM_FindMinMax)->Arg(7)->Arg(1024)->Arg(2047)->Arg(kLevelCount);