blob: cfa5df1a7e0c14e0e8fed195dfe8c2685d7d0d9c [file] [log] [blame]
// 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.
#include "parquet/level_conversion.h"
#include <algorithm>
#include <limits>
#if defined(ARROW_HAVE_BMI2)
#include <x86intrin.h>
#endif
#include "arrow/util/bit_util.h"
#include "arrow/util/logging.h"
#include "parquet/exception.h"
namespace parquet {
namespace internal {
namespace {
inline void CheckLevelRange(const int16_t* levels, int64_t num_levels,
const int16_t max_expected_level) {
int16_t min_level = std::numeric_limits<int16_t>::max();
int16_t max_level = std::numeric_limits<int16_t>::min();
for (int x = 0; x < num_levels; x++) {
min_level = std::min(levels[x], min_level);
max_level = std::max(levels[x], max_level);
}
if (ARROW_PREDICT_FALSE(num_levels > 0 &&
(min_level < 0 || max_level > max_expected_level))) {
throw ParquetException("definition level exceeds maximum");
}
}
#if !defined(ARROW_HAVE_AVX512)
inline void DefinitionLevelsToBitmapScalar(
const int16_t* def_levels, int64_t num_def_levels, const int16_t max_definition_level,
const int16_t max_repetition_level, int64_t* values_read, int64_t* null_count,
uint8_t* valid_bits, int64_t valid_bits_offset) {
// We assume here that valid_bits is large enough to accommodate the
// additional definition levels and the ones that have already been written
::arrow::internal::BitmapWriter valid_bits_writer(valid_bits, valid_bits_offset,
num_def_levels);
// TODO(itaiin): As an interim solution we are splitting the code path here
// between repeated+flat column reads, and non-repeated+nested reads.
// Those paths need to be merged in the future
for (int i = 0; i < num_def_levels; ++i) {
if (def_levels[i] == max_definition_level) {
valid_bits_writer.Set();
} else if (max_repetition_level > 0) {
// repetition+flat case
if (def_levels[i] == (max_definition_level - 1)) {
valid_bits_writer.Clear();
*null_count += 1;
} else {
continue;
}
} else {
// non-repeated+nested case
if (def_levels[i] < max_definition_level) {
valid_bits_writer.Clear();
*null_count += 1;
} else {
throw ParquetException("definition level exceeds maximum");
}
}
valid_bits_writer.Next();
}
valid_bits_writer.Finish();
*values_read = valid_bits_writer.position();
}
#endif
template <bool has_repeated_parent>
int64_t DefinitionLevelsBatchToBitmap(const int16_t* def_levels, const int64_t batch_size,
const int16_t required_definition_level,
::arrow::internal::FirstTimeBitmapWriter* writer) {
CheckLevelRange(def_levels, batch_size, required_definition_level);
uint64_t defined_bitmap =
internal::GreaterThanBitmap(def_levels, batch_size, required_definition_level - 1);
DCHECK_LE(batch_size, 64);
if (has_repeated_parent) {
#if defined(ARROW_HAVE_BMI2)
// This is currently a specialized code path assuming only (nested) lists
// present through the leaf (i.e. no structs). Upper level code only calls
// this method when the leaf-values are nullable (otherwise no spacing is needed),
// Because only nested lists exists it is sufficient to know that the field
// was either null or included it (i.e. definition level > max_definitation_level
// -2) If there where structs mixed in, we need to know the def_level of the
// repeated parent so we can check for def_level > "def level of repeated parent".
uint64_t present_bitmap = internal::GreaterThanBitmap(def_levels, batch_size,
required_definition_level - 2);
uint64_t selected_bits = _pext_u64(defined_bitmap, present_bitmap);
writer->AppendWord(selected_bits, ::arrow::BitUtil::PopCount(present_bitmap));
return ::arrow::BitUtil::PopCount(selected_bits);
#else
assert(false && "must not execute this without BMI2");
#endif
} else {
writer->AppendWord(defined_bitmap, batch_size);
return ::arrow::BitUtil::PopCount(defined_bitmap);
}
}
template <bool has_repeated_parent>
void DefinitionLevelsToBitmapSimd(const int16_t* def_levels, int64_t num_def_levels,
const int16_t required_definition_level,
int64_t* values_read, int64_t* null_count,
uint8_t* valid_bits, int64_t valid_bits_offset) {
constexpr int64_t kBitMaskSize = 64;
::arrow::internal::FirstTimeBitmapWriter writer(valid_bits,
/*start_offset=*/valid_bits_offset,
/*length=*/num_def_levels);
int64_t set_count = 0;
*values_read = 0;
while (num_def_levels > kBitMaskSize) {
set_count += DefinitionLevelsBatchToBitmap<has_repeated_parent>(
def_levels, kBitMaskSize, required_definition_level, &writer);
def_levels += kBitMaskSize;
num_def_levels -= kBitMaskSize;
}
set_count += DefinitionLevelsBatchToBitmap<has_repeated_parent>(
def_levels, num_def_levels, required_definition_level, &writer);
*values_read = writer.position();
*null_count += *values_read - set_count;
writer.Finish();
}
void DefinitionLevelsToBitmapLittleEndian(
const int16_t* def_levels, int64_t num_def_levels, const int16_t max_definition_level,
const int16_t max_repetition_level, int64_t* values_read, int64_t* null_count,
uint8_t* valid_bits, int64_t valid_bits_offset) {
if (max_repetition_level > 0) {
// This is a short term hack to prevent using the pext BMI2 instructions
// on non-intel platforms where performance is subpar.
// In the medium term we will hopefully be able to runtime dispatch
// to use this on intel only platforms that support pext.
#if defined(ARROW_HAVE_AVX512)
// BMI2 is required for efficient bit extraction.
DefinitionLevelsToBitmapSimd</*has_repeated_parent=*/true>(
def_levels, num_def_levels, max_definition_level, values_read, null_count,
valid_bits, valid_bits_offset);
#else
DefinitionLevelsToBitmapScalar(def_levels, num_def_levels, max_definition_level,
max_repetition_level, values_read, null_count,
valid_bits, valid_bits_offset);
#endif // ARROW_HAVE_BMI2
} else {
// No BMI2 intsturctions are used for non-repeated case.
DefinitionLevelsToBitmapSimd</*has_repeated_parent=*/false>(
def_levels, num_def_levels, max_definition_level, values_read, null_count,
valid_bits, valid_bits_offset);
}
}
} // namespace
void DefinitionLevelsToBitmap(const int16_t* def_levels, int64_t num_def_levels,
const int16_t max_definition_level,
const int16_t max_repetition_level, int64_t* values_read,
int64_t* null_count, uint8_t* valid_bits,
int64_t valid_bits_offset) {
#if ARROW_LITTLE_ENDIAN
DefinitionLevelsToBitmapLittleEndian(def_levels, num_def_levels, max_definition_level,
max_repetition_level, values_read, null_count,
valid_bits, valid_bits_offset);
#else
DefinitionLevelsToBitmapScalar(def_levels, num_def_levels, max_definition_level,
max_repetition_level, values_read, null_count,
valid_bits, valid_bits_offset);
#endif
}
} // namespace internal
} // namespace parquet