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
2 changes: 1 addition & 1 deletion cpp/src/arrow/util/bit_util_benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ static void BenchmarkBitmapVisitBitsetAnd(benchmark::State& state) {
static void BenchmarkBitmapVisitUInt8And(benchmark::State& state) {
BenchmarkAndImpl(state, [](const internal::Bitmap(&bitmaps)[2], internal::Bitmap* out) {
int64_t i = 0;
internal::Bitmap::VisitWords(bitmaps, [&](std::array<uint8_t, 2> uint8s) {
internal::Bitmap::VisitWords<uint8_t>(bitmaps, [&](std::array<uint8_t, 2> uint8s) {
reinterpret_cast<uint8_t*>(out->mutable_data())[i++] = uint8s[0] & uint8s[1];
});
});
Expand Down
20 changes: 10 additions & 10 deletions cpp/src/arrow/util/bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
#include <array>
#include <bitset>
#include <cassert>
#include <concepts>
#include <cstdint>
#include <cstring>
#include <memory>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>

#include "arrow/buffer.h"
Expand All @@ -36,7 +38,6 @@
#include "arrow/util/bitmap_writer.h"
#include "arrow/util/compare.h"
#include "arrow/util/endian.h"
#include "arrow/util/functional.h"
#include "arrow/util/string_util.h"
#include "arrow/util/visibility.h"

Expand Down Expand Up @@ -145,9 +146,8 @@ class ARROW_EXPORT Bitmap : public util::ToStringOstreamable<Bitmap>,
// carefully in other cases.
// For 2 bitmaps or less, and/or smaller bitmaps, see also VisitTwoBitBlocksVoid
// and BitmapUInt64Reader.
template <size_t N, typename Visitor,
typename Word = typename std::decay<
internal::call_traits::argument_type<0, Visitor&&>>::type::value_type>
template <typename Word = uint64_t, size_t N, typename Visitor>
requires std::same_as<void, std::invoke_result_t<Visitor, std::array<Word, N>&>>
static int64_t VisitWords(const Bitmap (&bitmaps_arg)[N], Visitor&& visitor) {
constexpr int64_t kBitWidth = sizeof(Word) * 8;

Expand Down Expand Up @@ -248,13 +248,12 @@ class ARROW_EXPORT Bitmap : public util::ToStringOstreamable<Bitmap>,
return min_offset;
}

template <size_t N, size_t M, typename ReaderT, typename WriterT, typename Visitor,
typename Word = typename std::decay<
internal::call_traits::argument_type<0, Visitor&&>>::type::value_type>
template <size_t N, size_t M, typename ReaderT, typename WriterT, typename Visitor>
static void RunVisitWordsAndWriteLoop(int64_t bit_length,
std::array<ReaderT, N>& readers,
std::array<WriterT, M>& writers,
Visitor&& visitor) {
using Word = decltype(readers[0].NextWord());
constexpr int64_t kBitWidth = sizeof(Word) * 8;

std::array<Word, N> visited_words;
Expand Down Expand Up @@ -318,6 +317,7 @@ class ARROW_EXPORT Bitmap : public util::ToStringOstreamable<Bitmap>,
/// may be offset within the first visited word, but words will otherwise contain
/// densely packed bits loaded from the bitmap. That offset within the first word is
/// returned.
///
/// Visitor is expected to have the following signature
/// [](const std::array<Word, N>& in_words, std::array<Word, M>* out_words){...}
///
Expand All @@ -326,9 +326,9 @@ class ARROW_EXPORT Bitmap : public util::ToStringOstreamable<Bitmap>,
// carefully in other cases.
// For 2 bitmaps or less, and/or smaller bitmaps, see also VisitTwoBitBlocksVoid
// and BitmapUInt64Reader.
template <size_t N, size_t M, typename Visitor,
typename Word = typename std::decay<
internal::call_traits::argument_type<0, Visitor&&>>::type::value_type>
template <typename Word = uint64_t, size_t N, size_t M, typename Visitor>
requires std::same_as<
void, std::invoke_result_t<Visitor, std::array<Word, N>&, std::array<Word, M>*>>
static void VisitWordsAndWrite(const std::array<Bitmap, N>& bitmaps_arg,
std::array<Bitmap, M>* out_bitmaps_arg,
Visitor&& visitor) {
Expand Down
5 changes: 3 additions & 2 deletions cpp/src/arrow/util/cache_benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ static void BenchmarkMemoize(benchmark::State& state, Memoized&& mem,
static void MemoizeLruCached(benchmark::State& state) {
const auto keys = MakeStrings(kCacheSize, state.range(0));
const auto values = MakeStrings(kCacheSize, state.range(1));
auto mem = MemoizeLru(Callable(values), kCacheSize);
auto mem = MemoizeLru<std::string>(Callable(values), kCacheSize);
BenchmarkMemoize(state, mem, keys);
}

Expand All @@ -135,7 +135,8 @@ static void MemoizeLruCachedThreadUnsafe(benchmark::State& state) {
const auto values = MakeStrings(kCacheSize, state.range(1));
// Emulate recommended usage of MemoizeLruCachedThreadUnsafe
// (the compiler is probably able to cache the TLS-looked up value, though)
thread_local auto mem = MemoizeLruThreadUnsafe(Callable(values), kCacheSize);
thread_local auto mem =
MemoizeLruThreadUnsafe<std::string>(Callable(values), kCacheSize);
BenchmarkMemoize(state, mem, keys);
}

Expand Down
37 changes: 18 additions & 19 deletions cpp/src/arrow/util/cache_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include <utility>
#include <vector>

#include "arrow/util/functional.h"
#include "arrow/util/logging.h"
#include "arrow/util/macros.h"

Expand Down Expand Up @@ -167,42 +166,42 @@ struct ThreadUnsafeMemoizer {
};

template <template <typename...> class Cache, template <typename...> class MemoizerType,
typename Func, typename Key = std::decay_t<call_traits::argument_type<0, Func>>,
typename Value = std::decay_t<std::invoke_result_t<Func, const Key&>>,
typename Memoizer = MemoizerType<Key, Value, Cache<Key, Value>, Func>,
typename RetType = typename Memoizer::RetType>
static std::function<RetType(const Key&)> Memoize(Func&& func, int32_t cache_capacity) {
typename Key, typename Func>
static auto Memoize(Func&& func, int32_t cache_capacity) {
using Value = std::decay_t<std::invoke_result_t<Func, const Key&>>;
using Memoizer = MemoizerType<Key, Value, Cache<Key, Value>, Func>;
using RetType = typename Memoizer::RetType;

// std::function<> requires copy constructibility
struct {
RetType operator()(const Key& key) const { return (*memoized_)(key); }
std::shared_ptr<Memoizer> memoized_;
} shared_memoized = {
std::make_shared<Memoizer>(std::forward<Func>(func), cache_capacity)};

return shared_memoized;
return std::function<RetType(const Key&)>(std::move(shared_memoized));

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.

Wrapping the result in a std::function adds a layer of indirection when calling operator() that might be expensive. Can we avoid this?

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.

Maybe I am understanding the code wrong, but I thought the change is equivalent.

The previous version had the function declaration:
static std::function<RetType(const Key&)> Memoize(...)

whereas now we have
static auto Memoize(...)

so all I did in the return statement was changing the implicit conversion to a std::function to an explicit one

}

} // namespace detail

// Apply a LRU memoization cache to a callable.
template <typename Func>
static auto MemoizeLru(Func&& func, int32_t cache_capacity)
-> decltype(detail::Memoize<LruCache, detail::ThreadSafeMemoizer>(
std::forward<Func>(func), cache_capacity)) {
return detail::Memoize<LruCache, detail::ThreadSafeMemoizer>(std::forward<Func>(func),
cache_capacity);
// `Key` is the type of the callable's (single) argument.
template <typename Key, typename Func>
requires std::is_invocable_v<Func, const Key&>
static auto MemoizeLru(Func&& func, int32_t cache_capacity) {
return detail::Memoize<LruCache, detail::ThreadSafeMemoizer, Key>(
std::forward<Func>(func), cache_capacity);
}

// Like MemoizeLru, but not thread-safe. This version allows for much faster
// lookups (more than 2x faster), but you'll have to manage thread safety yourself.
// A recommended usage is to declare per-thread caches using `thread_local`
// (see cache_benchmark.cc).
template <typename Func>
static auto MemoizeLruThreadUnsafe(Func&& func, int32_t cache_capacity)
-> decltype(detail::Memoize<LruCache, detail::ThreadUnsafeMemoizer>(
std::forward<Func>(func), cache_capacity)) {
return detail::Memoize<LruCache, detail::ThreadUnsafeMemoizer>(std::forward<Func>(func),
cache_capacity);
template <typename Key, typename Func>
requires std::is_invocable_v<Func, const Key&>
static auto MemoizeLruThreadUnsafe(Func&& func, int32_t cache_capacity) {
return detail::Memoize<LruCache, detail::ThreadUnsafeMemoizer, Key>(
std::forward<Func>(func), cache_capacity);
}

} // namespace internal
Expand Down
14 changes: 6 additions & 8 deletions cpp/src/arrow/util/cache_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,16 @@ struct Callable {
};

struct MemoizeLruFactory {
template <typename Func,
typename RetType = decltype(MemoizeLru(std::declval<Func>(), 0))>
RetType operator()(Func&& func, int32_t capacity) {
return MemoizeLru(std::forward<Func>(func), capacity);
template <typename Func>
auto operator()(Func&& func, int32_t capacity) {
return MemoizeLru<std::string>(std::forward<Func>(func), capacity);
}
};

struct MemoizeLruThreadUnsafeFactory {
template <typename Func,
typename RetType = decltype(MemoizeLruThreadUnsafe(std::declval<Func>(), 0))>
RetType operator()(Func&& func, int32_t capacity) {
return MemoizeLruThreadUnsafe(std::forward<Func>(func), capacity);
template <typename Func>
auto operator()(Func&& func, int32_t capacity) {
return MemoizeLruThreadUnsafe<std::string>(std::forward<Func>(func), capacity);
}
};

Expand Down
23 changes: 0 additions & 23 deletions cpp/src/arrow/util/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#pragma once

#include <memory>
#include <tuple>
#include <type_traits>

#include "arrow/result.h"
Expand All @@ -36,28 +35,6 @@ struct Empty {
}
};

/// Helper struct for examining lambdas and other callables.
/// TODO(ARROW-12655) support function pointers
struct call_traits {
public:
template <std::size_t I, typename F, typename R, typename... A>
static typename std::tuple_element<I, std::tuple<A...>>::type argument_type_impl(
R (F::*)(A...));

template <std::size_t I, typename F, typename R, typename... A>
static typename std::tuple_element<I, std::tuple<A...>>::type argument_type_impl(
R (F::*)(A...) const);

template <std::size_t I, typename F, typename R, typename... A>
static typename std::tuple_element<I, std::tuple<A...>>::type argument_type_impl(
R (F::*)(A...) &&);

/// If F is not overloaded, the argument types of its call operator can be
/// extracted via call_traits::argument_type<Index, F>
template <std::size_t I, typename F>
using argument_type = decltype(argument_type_impl<I>(&std::decay<F>::type::operator()));
};

/// A type erased callable object which may only be invoked once.
/// It can be constructed from any lambda which matches the provided call signature.
/// Invoking it results in destruction of the lambda, freeing any state/references
Expand Down
21 changes: 6 additions & 15 deletions cpp/src/arrow/util/future.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ struct SyncType<internal::Empty> {
using type = Status;
};

template <typename Fn>
using first_arg_is_status =
std::is_same<std::decay_t<internal::call_traits::argument_type<0, Fn>>, Status>;

template <typename Fn, typename Then, typename Else>
using if_has_no_args = std::conditional_t<std::is_invocable_v<Fn>, Then, Else>;

Expand Down Expand Up @@ -446,10 +442,12 @@ class [[nodiscard]] Future {
};
};

// conditional whether OnComplete is invokable with Status _and not with Result_
template <typename OnComplete>
using WrapOnComplete = typename std::conditional<
detail::first_arg_is_status<OnComplete>::value, WrapStatusyOnComplete,
WrapResultOnComplete>::type::template Callback<OnComplete>;
std::is_invocable_v<OnComplete, const Status&> &&
!std::is_invocable_v<OnComplete, const Result<ValueType>&>,
WrapStatusyOnComplete, WrapResultOnComplete>::type::template Callback<OnComplete>;

/// \brief Consumer API: Register a callback to run when this future completes
///
Expand Down Expand Up @@ -514,15 +512,8 @@ class [[nodiscard]] Future {
ContinuedFuture>::value,
"OnSuccess and OnFailure must continue with the same future type");

struct DummyOnSuccess {
void operator()(const T&);
};
using OnSuccessArg = typename std::decay<internal::call_traits::argument_type<
0, detail::if_has_no_args<OnSuccess, DummyOnSuccess, OnSuccess>>>::type;

static_assert(
!std::is_same<OnSuccessArg, typename EnsureResult<OnSuccessArg>::type>::value,
"OnSuccess' argument should not be a Result");
static_assert(!std::is_invocable_v<OnSuccess, const Result<T>&>,
"OnSuccess' argument should not be a Result");

void operator()(const Result<T>& result) && {
detail::ContinueFuture continue_future;
Expand Down
12 changes: 5 additions & 7 deletions cpp/src/arrow/util/future_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1539,13 +1539,11 @@ TEST(FnOnceTest, MoveOnlyDataType) {
return *i0.data + *i1.data + (i0.moves * 1000) + (i1.moves * 100);
};

using arg0 = call_traits::argument_type<0, decltype(fn)>;
using arg1 = call_traits::argument_type<1, decltype(fn)>;
using arg2 = call_traits::argument_type<2, decltype(fn)>;
static_assert(std::is_same<arg0, const MoveOnlyDataType&>::value, "");
static_assert(std::is_same<arg1, MoveOnlyDataType>::value, "");
static_assert(std::is_same<arg2, std::string>::value,
"should not add a && to the call type (demanding rvalue unnecessarily)");
static_assert(std::is_invocable_r_v<int, decltype(fn), const MoveOnlyDataType&,
MoveOnlyDataType, std::string&>);
// a move-only by-value argument must be moved in
static_assert(!std::is_invocable_v<decltype(fn), const MoveOnlyDataType&,
MoveOnlyDataType&, std::string&>);

MoveOnlyDataType i0{1}, i1{41};
std::string copyable = "";
Expand Down
Loading