mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 23:52:03 +00:00
Code cleanups after #4439
This commit is contained in:
parent
445f51c01e
commit
661c840fbe
@ -68,12 +68,33 @@ struct ColumnVector<T>::greater
|
||||
bool operator()(size_t lhs, size_t rhs) const { return CompareHelper<T>::greater(parent.data[lhs], parent.data[rhs], nan_direction_hint); }
|
||||
};
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
struct ValueWithIndex
|
||||
{
|
||||
T value;
|
||||
UInt32 index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RadixSortTraits : RadixSortNumTraits<T>
|
||||
{
|
||||
using Element = ValueWithIndex<T>;
|
||||
static T & extractKey(Element & elem) { return elem.value; }
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ColumnVector<T>::getPermutation(bool reverse, size_t limit, int nan_direction_hint, IColumn::Permutation & res) const
|
||||
{
|
||||
size_t s = data.size();
|
||||
res.resize(s);
|
||||
|
||||
if (s == 0)
|
||||
return;
|
||||
|
||||
if (limit >= s)
|
||||
limit = 0;
|
||||
|
||||
@ -89,34 +110,79 @@ void ColumnVector<T>::getPermutation(bool reverse, size_t limit, int nan_directi
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr ((std::is_signed_v<T> || std::is_unsigned_v<T>) && !std::is_same_v<T, UInt128>)
|
||||
/// A case for radix sort
|
||||
if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, UInt128>)
|
||||
{
|
||||
PaddedPODArray<std::pair<T, size_t>> pairs(s);
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
pairs[i] = {data[i], i};
|
||||
/// Thresholds on size. Lower threshold is arbitrary. Upper threshold is chosen by the type for histogram counters.
|
||||
if (s >= 256 && s <= std::numeric_limits<UInt32>::max())
|
||||
{
|
||||
PaddedPODArray<ValueWithIndex<T>> pairs(s);
|
||||
for (UInt32 i = 0; i < s; ++i)
|
||||
pairs[i] = {data[i], i};
|
||||
|
||||
radixSort(pairs.data(), s, nan_direction_hint);
|
||||
RadixSort<RadixSortTraits<T>>::execute(pairs.data(), s);
|
||||
|
||||
if (reverse)
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[s - 1 - i] = pairs[i].second;
|
||||
else
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = pairs[i].second;
|
||||
/// Radix sort treats all NaNs to be greater than all numbers.
|
||||
/// If the user needs the opposite, we must move them accordingly.
|
||||
size_t nans_to_move = 0;
|
||||
if (std::is_floating_point_v<T> && nan_direction_hint < 0)
|
||||
{
|
||||
for (ssize_t i = s - 1; i >= 0; --i)
|
||||
{
|
||||
if (isNaN(pairs[i].value))
|
||||
++nans_to_move;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
{
|
||||
if (nans_to_move)
|
||||
{
|
||||
for (size_t i = 0; i < s - nans_to_move; ++i)
|
||||
res[i] = pairs[s - nans_to_move - 1 - i].index;
|
||||
for (size_t i = s - nans_to_move; i < s; ++i)
|
||||
res[i] = pairs[s - 1 - (i - (s - nans_to_move))].index;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[s - 1 - i] = pairs[i].index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nans_to_move)
|
||||
{
|
||||
for (size_t i = 0; i < nans_to_move; ++i)
|
||||
res[i] = pairs[i + s - nans_to_move].index;
|
||||
for (size_t i = nans_to_move; i < s; ++i)
|
||||
res[i] = pairs[i - nans_to_move].index;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = pairs[i].index;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Default sorting algorithm.
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = i;
|
||||
|
||||
if (reverse)
|
||||
pdqsort(res.begin(), res.end(), greater(*this, nan_direction_hint));
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
res[i] = i;
|
||||
|
||||
if (reverse)
|
||||
pdqsort(res.begin(), res.end(), greater(*this, nan_direction_hint));
|
||||
else
|
||||
pdqsort(res.begin(), res.end(), less(*this, nan_direction_hint));
|
||||
}
|
||||
pdqsort(res.begin(), res.end(), less(*this, nan_direction_hint));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
const char * ColumnVector<T>::getFamilyName() const
|
||||
{
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <ext/bit_cast.h>
|
||||
#include <Core/Types.h>
|
||||
@ -68,15 +67,15 @@ struct RadixSortFloatTransform
|
||||
};
|
||||
|
||||
|
||||
template <typename _Element, typename _Key = _Element>
|
||||
template <typename TElement>
|
||||
struct RadixSortFloatTraits
|
||||
{
|
||||
using Element = _Element; /// The type of the element. It can be a structure with a key and some other payload. Or just a key.
|
||||
using Key = _Key; /// The key to sort.
|
||||
using Element = TElement; /// The type of the element. It can be a structure with a key and some other payload. Or just a key.
|
||||
using Key = Element; /// The key to sort by.
|
||||
using CountType = uint32_t; /// Type for calculating histograms. In the case of a known small number of elements, it can be less than size_t.
|
||||
|
||||
/// The type to which the key is transformed to do bit operations. This UInt is the same size as the key.
|
||||
using KeyBits = std::conditional_t<sizeof(_Key) == 8, uint64_t, uint32_t>;
|
||||
using KeyBits = std::conditional_t<sizeof(Key) == 8, uint64_t, uint32_t>;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8; /// With what pieces of the key, in bits, to do one pass - reshuffle of the array.
|
||||
|
||||
@ -89,30 +88,7 @@ struct RadixSortFloatTraits
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem)
|
||||
{
|
||||
if constexpr (std::is_same_v<Element, Key>)
|
||||
return elem;
|
||||
else
|
||||
return *reinterpret_cast<Key *>(&elem);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Float>
|
||||
struct RadixSortPairFloatKeyTraits
|
||||
{
|
||||
using Element = std::pair<Float, size_t>;
|
||||
using Key = Float;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = std::conditional_t<sizeof(Float) == 8, uint64_t, uint32_t>;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortFloatTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem) { return elem.first; }
|
||||
static Key & extractKey(Element & elem) { return elem; }
|
||||
};
|
||||
|
||||
template <typename KeyBits>
|
||||
@ -135,92 +111,49 @@ struct RadixSortSignedTransform
|
||||
};
|
||||
|
||||
|
||||
template <typename _Element, typename _Key = _Element>
|
||||
template <typename TElement>
|
||||
struct RadixSortUIntTraits
|
||||
{
|
||||
using Element = _Element;
|
||||
using Key = _Key;
|
||||
using Element = TElement;
|
||||
using Key = Element;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = _Key;
|
||||
using KeyBits = Key;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortIdentityTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem)
|
||||
{
|
||||
if constexpr (std::is_same_v<Element, Key>)
|
||||
return elem;
|
||||
else
|
||||
return *reinterpret_cast<Key *>(&elem);
|
||||
}
|
||||
static Key & extractKey(Element & elem) { return elem; }
|
||||
};
|
||||
|
||||
template <typename UInt>
|
||||
struct RadixSortPairUIntKeyTraits
|
||||
{
|
||||
using Element = std::pair<UInt, size_t>;
|
||||
using Key = UInt;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = UInt;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortIdentityTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem) { return elem.first; }
|
||||
};
|
||||
|
||||
template <typename _Element, typename _Key = _Element>
|
||||
template <typename TElement>
|
||||
struct RadixSortIntTraits
|
||||
{
|
||||
using Element = _Element;
|
||||
using Key = _Key;
|
||||
using Element = TElement;
|
||||
using Key = Element;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = std::make_unsigned_t<_Key>;
|
||||
using KeyBits = std::make_unsigned_t<Key>;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortSignedTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem)
|
||||
{
|
||||
if constexpr (std::is_same_v<Element, Key>)
|
||||
return elem;
|
||||
else
|
||||
return *reinterpret_cast<Key *>(&elem);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Int>
|
||||
struct RadixSortPairIntKeyTraits
|
||||
{
|
||||
using Element = std::pair<Int, size_t>;
|
||||
using Key = Int;
|
||||
using CountType = uint32_t;
|
||||
using KeyBits = std::make_unsigned_t<Int>;
|
||||
|
||||
static constexpr size_t PART_SIZE_BITS = 8;
|
||||
|
||||
using Transform = RadixSortSignedTransform<KeyBits>;
|
||||
using Allocator = RadixSortMallocAllocator;
|
||||
|
||||
/// The function to get the key from an array element.
|
||||
static Key & extractKey(Element & elem) { return elem.first; }
|
||||
static Key & extractKey(Element & elem) { return elem; }
|
||||
};
|
||||
|
||||
|
||||
// Allow std::pair copying
|
||||
#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 8)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#endif
|
||||
template <typename T>
|
||||
using RadixSortNumTraits =
|
||||
std::conditional_t<std::is_integral_v<T>,
|
||||
std::conditional_t<std::is_unsigned_v<T>,
|
||||
RadixSortUIntTraits<T>,
|
||||
RadixSortIntTraits<T>>,
|
||||
RadixSortFloatTraits<T>>;
|
||||
|
||||
|
||||
template <typename Traits>
|
||||
struct RadixSort
|
||||
{
|
||||
@ -268,8 +201,8 @@ public:
|
||||
if (!Traits::Transform::transform_is_simple)
|
||||
Traits::extractKey(arr[i]) = bitsToKey(Traits::Transform::forward(keyToBits(Traits::extractKey(arr[i]))));
|
||||
|
||||
for (size_t j = 0; j < NUM_PASSES; ++j)
|
||||
++histograms[j * HISTOGRAM_SIZE + getPart(j, keyToBits(Traits::extractKey(arr[i])))];
|
||||
for (size_t pass = 0; pass < NUM_PASSES; ++pass)
|
||||
++histograms[pass * HISTOGRAM_SIZE + getPart(pass, keyToBits(Traits::extractKey(arr[i])))];
|
||||
}
|
||||
|
||||
{
|
||||
@ -278,31 +211,31 @@ public:
|
||||
|
||||
for (size_t i = 0; i < HISTOGRAM_SIZE; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < NUM_PASSES; ++j)
|
||||
for (size_t pass = 0; pass < NUM_PASSES; ++pass)
|
||||
{
|
||||
size_t tmp = histograms[j * HISTOGRAM_SIZE + i] + sums[j];
|
||||
histograms[j * HISTOGRAM_SIZE + i] = sums[j] - 1;
|
||||
sums[j] = tmp;
|
||||
size_t tmp = histograms[pass * HISTOGRAM_SIZE + i] + sums[pass];
|
||||
histograms[pass * HISTOGRAM_SIZE + i] = sums[pass] - 1;
|
||||
sums[pass] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the elements in the order starting from the least bit piece, and then do a few passes on the number of pieces.
|
||||
for (size_t j = 0; j < NUM_PASSES; ++j)
|
||||
for (size_t pass = 0; pass < NUM_PASSES; ++pass)
|
||||
{
|
||||
Element * writer = j % 2 ? arr : swap_buffer;
|
||||
Element * reader = j % 2 ? swap_buffer : arr;
|
||||
Element * writer = pass % 2 ? arr : swap_buffer;
|
||||
Element * reader = pass % 2 ? swap_buffer : arr;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
size_t pos = getPart(j, keyToBits(Traits::extractKey(reader[i])));
|
||||
size_t pos = getPart(pass, keyToBits(Traits::extractKey(reader[i])));
|
||||
|
||||
/// Place the element on the next free position.
|
||||
auto & dest = writer[++histograms[j * HISTOGRAM_SIZE + pos]];
|
||||
auto & dest = writer[++histograms[pass * HISTOGRAM_SIZE + pos]];
|
||||
dest = reader[i];
|
||||
|
||||
/// On the last pass, we do the reverse transformation.
|
||||
if (!Traits::Transform::transform_is_simple && j == NUM_PASSES - 1)
|
||||
if (!Traits::Transform::transform_is_simple && pass == NUM_PASSES - 1)
|
||||
Traits::extractKey(dest) = bitsToKey(Traits::Transform::backward(keyToBits(Traits::extractKey(reader[i]))));
|
||||
}
|
||||
}
|
||||
@ -314,80 +247,14 @@ public:
|
||||
|
||||
allocator.deallocate(swap_buffer, size * sizeof(Element));
|
||||
}
|
||||
|
||||
// For floating point types
|
||||
// Radix sort sometimes incorrectly handles NaNs
|
||||
// Will move them to the right place
|
||||
static void fixNanOrder(Element * arr, size_t size, int nan_direction_hint)
|
||||
{
|
||||
if (nan_direction_hint < 0)
|
||||
{
|
||||
size_t nans_count = std::count_if(arr, arr + size, [](Element d) {return std::isnan(Traits::extractKey(d));});
|
||||
std::vector<Element> nans(nans_count);
|
||||
std::copy(arr + size - nans_count, arr + size, nans.data());
|
||||
std::copy_backward(arr, arr + size - nans_count, arr + size);
|
||||
std::copy(nans.data(), nans.data() + nans_count, arr);
|
||||
}
|
||||
}
|
||||
};
|
||||
#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 8)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
/// Helper functions for numeric types.
|
||||
/// Use RadixSort with custom traits for complex types instead.
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_unsigned_v<T> && std::is_integral_v<T>, void>
|
||||
radixSort(T * arr, size_t size, int /*nan_direction_hint*/=1)
|
||||
void radixSort(T * arr, size_t size)
|
||||
{
|
||||
RadixSort<RadixSortUIntTraits<T>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_signed_v<T> && std::is_integral_v<T>, void>
|
||||
radixSort(T * arr, size_t size, int /*nan_direction_hint*/=1)
|
||||
{
|
||||
RadixSort<RadixSortIntTraits<T>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T>, void>
|
||||
radixSort(T * arr, size_t size, int nan_direction_hint=1)
|
||||
{
|
||||
RadixSort<RadixSortFloatTraits<T>>::execute(arr, size);
|
||||
RadixSort<RadixSortFloatTraits<T>>::fixNanOrder(arr, size, nan_direction_hint);
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Key>
|
||||
std::enable_if_t<std::is_integral_v<_Key>, void>
|
||||
radixSort(_Element * arr, size_t size)
|
||||
{
|
||||
return RadixSort<RadixSortUIntTraits<_Element, _Key>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Key>
|
||||
std::enable_if_t<std::is_floating_point_v<_Key>, void>
|
||||
radixSort(_Element * arr, size_t size)
|
||||
{
|
||||
return RadixSort<RadixSortFloatTraits<_Element, _Key>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_unsigned_v<T> && !std::is_floating_point_v<T>, void>
|
||||
radixSort(std::pair<T, size_t> * arr, size_t size, int /*nan_direction_hint*/=1)
|
||||
{
|
||||
RadixSort<RadixSortPairUIntKeyTraits<T>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_signed_v<T> && !std::is_floating_point_v<T>, void>
|
||||
radixSort(std::pair<T, size_t> * arr, size_t size, int /*nan_direction_hint*/=1)
|
||||
{
|
||||
RadixSort<RadixSortPairIntKeyTraits<T>>::execute(arr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T>, void>
|
||||
radixSort(std::pair<T, size_t> * arr, size_t size, int nan_direction_hint=1)
|
||||
{
|
||||
RadixSort<RadixSortPairFloatKeyTraits<T>>::execute(arr, size);
|
||||
RadixSort<RadixSortPairFloatKeyTraits<T>>::fixNanOrder(arr, size, nan_direction_hint);
|
||||
RadixSort<RadixSortNumTraits<T>>::execute(arr, size);
|
||||
}
|
||||
|
@ -40,11 +40,11 @@ struct RowRefList : RowRef
|
||||
* references that can be returned by the lookup methods
|
||||
*/
|
||||
|
||||
template <typename _Entry, typename _Key>
|
||||
template <typename TEntry, typename TKey>
|
||||
class SortedLookupVector
|
||||
{
|
||||
public:
|
||||
using Base = std::vector<_Entry>;
|
||||
using Base = std::vector<TEntry>;
|
||||
|
||||
// First stage, insertions into the vector
|
||||
template <typename U, typename ... TAllocatorParams>
|
||||
@ -55,7 +55,7 @@ public:
|
||||
}
|
||||
|
||||
// Transition into second stage, ensures that the vector is sorted
|
||||
typename Base::const_iterator upper_bound(const _Entry & k)
|
||||
typename Base::const_iterator upper_bound(const TEntry & k)
|
||||
{
|
||||
sort();
|
||||
return std::upper_bound(array.cbegin(), array.cend(), k);
|
||||
@ -70,6 +70,12 @@ private:
|
||||
Base array;
|
||||
mutable std::mutex lock;
|
||||
|
||||
struct RadixSortTraits : RadixSortNumTraits<TKey>
|
||||
{
|
||||
using Element = TEntry;
|
||||
static TKey & extractKey(Element & elem) { return elem.asof_value; }
|
||||
};
|
||||
|
||||
// Double checked locking with SC atomics works in C++
|
||||
// https://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/
|
||||
// The first thread that calls one of the lookup methods sorts the data
|
||||
@ -82,14 +88,14 @@ private:
|
||||
std::lock_guard<std::mutex> l(lock);
|
||||
if (!sorted.load(std::memory_order_relaxed))
|
||||
{
|
||||
/// TODO: It has been tested only for UInt32 yet. It needs to check UInt64, Float32/64.
|
||||
if constexpr (std::is_same_v<_Key, UInt32>)
|
||||
if (!array.empty())
|
||||
{
|
||||
if (!array.empty())
|
||||
radixSort<_Entry, _Key>(&array[0], array.size());
|
||||
/// TODO: It has been tested only for UInt32 yet. It needs to check UInt64, Float32/64.
|
||||
if constexpr (std::is_same_v<TKey, UInt32>)
|
||||
RadixSort<RadixSortTraits>::execute(&array[0], array.size());
|
||||
else
|
||||
std::sort(array.begin(), array.end());
|
||||
}
|
||||
else
|
||||
std::sort(array.begin(), array.end());
|
||||
|
||||
sorted.store(true, std::memory_order_release);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user