/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed 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 <folly/lang/ToAscii.h>
#define __STDC_FORMAT_MACROS
#include <cinttypes>
#include <fmt/format.h>
#include <folly/Benchmark.h>
#include <folly/lang/Keep.h>
#include <folly/portability/FmtCompile.h>
#if __has_include(<charconv>)
#include <charconv>
#endif
using abc = folly::to_ascii_alphabet_lower;
#define FOLLY_TO_ASCII_BENCH_CHECK_SIZE(impl, base) \
extern "C" FOLLY_KEEP size_t check_to_ascii_size_##impl##_##base( \
uint64_t v) { \
return folly::detail::to_ascii_size_##impl<base>(v); \
}
#define FOLLY_TO_ASCII_BENCH_CHECK_WITH(impl, base) \
extern "C" FOLLY_KEEP void check_bound_to_ascii_with_##impl##_##base( \
char* buf, size_t size, uint64_t v) { \
folly::detail::to_ascii_with_##impl<base, abc>(buf, size, v); \
} \
extern "C" FOLLY_KEEP size_t check_return_to_ascii_with_##impl##_##base( \
char* buf, uint64_t v) { \
auto const size = folly::to_ascii_size<base>(v); \
folly::detail::to_ascii_with_##impl<base, abc>(buf, size, v); \
return size; \
}
#define FOLLY_TO_ASCII_BENCH_CHECK(base) \
extern "C" FOLLY_KEEP size_t check_to_ascii_range_##base( \
char* outb, char* oute, uint64_t v) { \
return folly::detail::to_ascii_with_route<base, abc>(outb, oute, v); \
} \
extern "C" FOLLY_KEEP size_t check_to_ascii_array_##base( \
char(&out)[(folly::to_ascii_size_max<base, uint64_t>)], uint64_t v) { \
return folly::detail::to_ascii_with_route<base, abc>(out, v); \
}
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(imuls, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(imuls, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(imuls, 8)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(idivs, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(idivs, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(idivs, 8)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(array, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(array, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(array, 8)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(clzll, 16)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(clzll, 10)
FOLLY_TO_ASCII_BENCH_CHECK_SIZE(clzll, 8)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(basic, 16)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(basic, 10)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(basic, 8)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(array, 16)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(array, 10)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(array, 8)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(table, 16)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(table, 10)
FOLLY_TO_ASCII_BENCH_CHECK_WITH(table, 8)
FOLLY_TO_ASCII_BENCH_CHECK(16)
FOLLY_TO_ASCII_BENCH_CHECK(10)
FOLLY_TO_ASCII_BENCH_CHECK(8)
namespace {
template <uint64_t Base>
struct inputs;
template <>
struct inputs<10> {
static constexpr uint64_t const data[21] = {
0,
1,
12,
123,
1234,
12345,
123456,
1234567,
12345678,
123456789,
1234567890,
12345678901,
123456789012,
1234567890123,
12345678901234,
123456789012345,
1234567890123456,
12345678901234567,
123456789012345678,
1234567890123456789,
12345678901234567890u,
};
};
constexpr uint64_t const inputs<10>::data[21];
template <>
struct inputs<16> {
static constexpr uint64_t const data[17] = {
0x0,
0x1,
0x12,
0x123,
0x1234,
0x12345,
0x123456,
0x1234567,
0x12345678,
0x123456789,
0x123456789a,
0x123456789ab,
0x123456789abc,
0x123456789abcd,
0x123456789abcde,
0x123456789abcdef,
0x123456789abcdef0,
};
};
constexpr uint64_t const inputs<16>::data[17];
} // namespace
static void fill_zero(char* out, size_t size, size_t index) {
(void)index;
std::memset(out, 0, size);
}
template <typename... A>
static void call_to_chars(A... a) {
std::to_chars(a...);
}
template <uint64_t Base, typename F>
static void to_ascii_size_go(size_t iters, size_t index, F f) {
while (iters--) {
auto const v = inputs<Base>::data[index] + (iters % 8);
auto const size = [&]() FOLLY_NOINLINE { return f(v); }();
folly::doNotOptimizeAway(size);
}
}
template <uint64_t Base, typename F>
static void to_ascii_go(size_t iters, size_t index, F f) {
char out[1 + folly::to_ascii_size_max<Base, uint64_t>];
while (iters--) {
auto const v = inputs<Base>::data[index] + (iters % 8);
auto const size = folly::to_ascii_size<Base>(v);
[&]() FOLLY_NOINLINE { f(&out[0], size, v); }();
}
folly::doNotOptimizeAway(out);
}
// to_ascii_size<10>
static void fmt_formatted_size_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return fmt::formatted_size(FOLLY_FMT_COMPILE("{}"), v);
});
}
static void to_ascii_size_imuls_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_imuls<10>(v);
});
}
static void to_ascii_size_idivs_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_idivs<10>(v);
});
}
static void to_ascii_size_array_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_array<10>(v);
});
}
static void to_ascii_size_clzll_10(size_t iters, size_t index) {
to_ascii_size_go<10>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_clzll<10>(v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fmt_formatted_size_10, index) \
BENCHMARK_PARAM(to_ascii_size_imuls_10, index) \
BENCHMARK_PARAM(to_ascii_size_idivs_10, index) \
BENCHMARK_PARAM(to_ascii_size_array_10, index) \
BENCHMARK_PARAM(to_ascii_size_clzll_10, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
BENCHMARK_GROUP(17)
BENCHMARK_GROUP(18)
BENCHMARK_GROUP(19)
BENCHMARK_GROUP(20)
#undef BENCHMARK_GROUP
BENCHMARK_DRAW_LINE();
// to_ascii_size<16>
static void fmt_formatted_size_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return fmt::formatted_size(FOLLY_FMT_COMPILE("{:x}"), v);
});
}
static void to_ascii_size_imuls_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_imuls<16>(v);
});
}
static void to_ascii_size_idivs_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_idivs<16>(v);
});
}
static void to_ascii_size_array_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_array<16>(v);
});
}
static void to_ascii_size_clzll_16(size_t iters, size_t index) {
to_ascii_size_go<16>(iters, index, [](auto v) {
return folly::detail::to_ascii_size_clzll<16>(v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fmt_formatted_size_16, index) \
BENCHMARK_PARAM(to_ascii_size_imuls_16, index) \
BENCHMARK_PARAM(to_ascii_size_idivs_16, index) \
BENCHMARK_PARAM(to_ascii_size_array_16, index) \
BENCHMARK_PARAM(to_ascii_size_clzll_16, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
#undef BENCHMARK_GROUP
BENCHMARK_DRAW_LINE();
// to_ascii<10>
static void fill_zero_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
fill_zero(out, size, v);
});
}
static void libc_snprintf_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
std::snprintf(out, size, "%" PRIu64, v);
});
}
static void std_to_string_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
auto result = std::to_string(v);
std::memcpy(out, &result[0], size);
});
}
static void std_to_chars__10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
call_to_chars(out, out + size, v, 10);
});
}
static void fmt_format_to_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
(void)size;
fmt::format_to(out, FMT_COMPILE("{}"), v);
});
}
static void to_ascii_basic_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_basic<10, abc>(out, size, v);
});
}
static void to_ascii_array_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_array<10, abc>(out, size, v);
});
}
static void to_ascii_table_10(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_table<10, abc>(out, size, v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fill_zero_10, index) \
BENCHMARK_PARAM(libc_snprintf_10, index) \
BENCHMARK_PARAM(std_to_string_10, index) \
BENCHMARK_PARAM(std_to_chars__10, index) \
BENCHMARK_PARAM(fmt_format_to_10, index) \
BENCHMARK_PARAM(to_ascii_basic_10, index) \
BENCHMARK_PARAM(to_ascii_array_10, index) \
BENCHMARK_PARAM(to_ascii_table_10, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
BENCHMARK_GROUP(17)
BENCHMARK_GROUP(18)
BENCHMARK_GROUP(19)
BENCHMARK_GROUP(20)
#undef BENCHMARK_GROUP
BENCHMARK_DRAW_LINE();
// to_ascii<16>
static void fill_zero_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
fill_zero(out, size, v);
});
}
static void libc_snprintf_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
std::snprintf(out, size, "%" PRIu64 "x", v);
});
}
static void std_to_chars__16(size_t iters, size_t index) {
to_ascii_go<10>(iters, index, [](auto out, auto size, auto v) {
call_to_chars(out, out + size, v, 16);
});
}
static void fmt_format_to_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
(void)size;
fmt::format_to(out, FMT_COMPILE("{:x}"), v);
});
}
static void to_ascii_basic_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_basic<16, abc>(out, size, v);
});
}
static void to_ascii_array_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_array<16, abc>(out, size, v);
});
}
static void to_ascii_table_16(size_t iters, size_t index) {
to_ascii_go<16>(iters, index, [](auto out, auto size, auto v) {
folly::detail::to_ascii_with_table<16, abc>(out, size, v);
});
}
#define BENCHMARK_GROUP(index) \
BENCHMARK_DRAW_LINE(); \
BENCHMARK_PARAM(fill_zero_16, index) \
BENCHMARK_PARAM(libc_snprintf_16, index) \
BENCHMARK_PARAM(std_to_chars__16, index) \
BENCHMARK_PARAM(fmt_format_to_16, index) \
BENCHMARK_PARAM(to_ascii_basic_16, index) \
BENCHMARK_PARAM(to_ascii_array_16, index) \
BENCHMARK_PARAM(to_ascii_table_16, index)
BENCHMARK_GROUP(1)
BENCHMARK_GROUP(2)
BENCHMARK_GROUP(3)
BENCHMARK_GROUP(4)
BENCHMARK_GROUP(5)
BENCHMARK_GROUP(6)
BENCHMARK_GROUP(7)
BENCHMARK_GROUP(8)
BENCHMARK_GROUP(9)
BENCHMARK_GROUP(10)
BENCHMARK_GROUP(11)
BENCHMARK_GROUP(12)
BENCHMARK_GROUP(13)
BENCHMARK_GROUP(14)
BENCHMARK_GROUP(15)
BENCHMARK_GROUP(16)
#undef BENCHMARK_GROUP
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
folly::runBenchmarks();
return 0;
}