Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cpp/src/parquet/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions cpp/src/parquet/level_comparison.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);

Expand All @@ -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) //
};
}
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/parquet/level_comparison.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 0 additions & 4 deletions cpp/src/parquet/level_comparison_avx2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is FindMinMaxImpl still in the codebase? If so, can we remove it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It cannot be removed, ARROW_USER_SIMD_LEVEL=NONE would still require it.

}

} // namespace internal
} // namespace parquet
1 change: 0 additions & 1 deletion cpp/src/parquet/level_comparison_avx2_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
41 changes: 41 additions & 0 deletions cpp/src/parquet/level_comparison_simd_128.cc
Original file line number Diff line number Diff line change
@@ -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<ARCH_PLATFORM>(levels, num_levels);
}

} // namespace parquet::internal

# undef ARCH_PLATFORM
# undef FIND_MIN_MAX_PLATFORM
#endif // FIND_MIN_MAX_PLATFORM
39 changes: 39 additions & 0 deletions cpp/src/parquet/level_comparison_simd_256.cc
Original file line number Diff line number Diff line change
@@ -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<ARCH_PLATFORM>(levels, num_levels);
}

} // namespace parquet::internal

#undef ARCH_PLATFORM
#undef FIND_MIN_MAX_PLATFORM
38 changes: 38 additions & 0 deletions cpp/src/parquet/level_comparison_simd_internal.h
Original file line number Diff line number Diff line change
@@ -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 <cstdint>

#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
56 changes: 56 additions & 0 deletions cpp/src/parquet/level_comparison_simd_kernel_internal.h
Original file line number Diff line number Diff line change
@@ -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 <xsimd/xsimd.hpp>

#include <algorithm>
#include <cstdint>
#include <limits>

#include "parquet/level_comparison.h"

namespace parquet::internal {

template <typename Arch>
MinMax FindMinMaxSimd(const int16_t* levels, int64_t num_levels) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AntoinePrv Do you want to take a look at this implementation?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable to me. Cannot say if it is optimal (perhaps a manual unroll could help because of the loop-carried dependency, but perhaps the compiler can tell it is associative), but if it improves then it's good!
We might be able to get a generic version of this in https://github.com/xtensor-stack/xsimd-algorithm (yet to put proper benchmarks and all).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ursabot never finished, so please run

build/release/parquet-arrow-reader-writer-benchmark \
   --benchmark_filter='BM_ReadColumn<true,'

I would like to see some independent numbers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using batch = xsimd::batch<int16_t, Arch>;
constexpr int64_t kLanes = static_cast<int64_t>(batch::size);

MinMax out{std::numeric_limits<int16_t>::max(), std::numeric_limits<int16_t>::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
20 changes: 20 additions & 0 deletions cpp/src/parquet/level_conversion_benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <vector>

#include "benchmark/benchmark.h"
#include "parquet/level_comparison.h"
#include "parquet/level_conversion.h"

constexpr int64_t kLevelCount = 2048;
Expand Down Expand Up @@ -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<int16_t> levels(/*count=*/static_cast<size_t>(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);
Loading