/*
* 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/Format.h>
#include <utility>
#include <glog/logging.h>
#include <folly/Benchmark.h>
#include <folly/FBVector.h>
#include <folly/Utility.h>
#include <folly/init/Init.h>
#include <folly/json/dynamic.h>
#include <folly/json/json.h>
FOLLY_GNU_DISABLE_WARNING("-Wdeprecated-declarations")
using namespace folly;
namespace {
std::array<char, 300> bigBuf;
std::string getShortString() {
return "ABCDEFGHIJ";
}
std::string getLongString() {
return std::string(256, 'A');
}
} // namespace
BENCHMARK(octal_snprintf, iters) {
while (iters--) {
snprintf(
bigBuf.data(), bigBuf.size(), "%o", static_cast<unsigned int>(iters));
}
}
BENCHMARK_RELATIVE(octal_uintToOctal, iters) {
while (iters--) {
detail::uintToOctal(
bigBuf.data(),
detail::kMaxOctalLength,
static_cast<unsigned int>(iters));
}
}
BENCHMARK_DRAW_LINE();
BENCHMARK(hex_snprintf, iters) {
while (iters--) {
snprintf(
bigBuf.data(), bigBuf.size(), "%x", static_cast<unsigned int>(iters));
}
}
BENCHMARK_RELATIVE(hex_uintToHex, iters) {
while (iters--) {
detail::uintToHexLower(
bigBuf.data(), detail::kMaxHexLength, static_cast<unsigned int>(iters));
}
}
BENCHMARK_DRAW_LINE();
BENCHMARK(intAppend_snprintf) {
fbstring out;
for (int i = -1000; i < 1000; i++) {
snprintf(bigBuf.data(), bigBuf.size(), "%d", i);
out.append(bigBuf.data());
}
}
BENCHMARK_RELATIVE(intAppend_to) {
fbstring out;
for (int i = -1000; i < 1000; i++) {
toAppend(i, &out);
}
}
BENCHMARK_RELATIVE(intAppend_format) {
fbstring out;
for (int i = -1000; i < 1000; i++) {
format(&out, "{}", i);
}
}
BENCHMARK_DRAW_LINE();
template <size_t... Indexes>
int snprintf20Numbers(int i, std::index_sequence<Indexes...>) {
static_assert(20 == sizeof...(Indexes), "Must have exactly 20 indexes");
return snprintf(
bigBuf.data(),
bigBuf.size(),
"%d %d %d %d %d"
"%d %d %d %d %d"
"%d %d %d %d %d"
"%d %d %d %d %d",
(i + static_cast<int>(Indexes))...);
}
BENCHMARK(bigFormat_snprintf, iters) {
while (iters--) {
for (int i = -100; i < 100; i++) {
snprintf20Numbers(i, std::make_index_sequence<20>());
}
}
}
template <size_t... Indexes>
decltype(auto) format20Numbers(int i, std::index_sequence<Indexes...>) {
static_assert(20 == sizeof...(Indexes), "Must have exactly 20 indexes");
return format(
"{} {} {} {} {}"
"{} {} {} {} {}"
"{} {} {} {} {}"
"{} {} {} {} {}",
(i + static_cast<int>(Indexes))...);
}
BENCHMARK_RELATIVE(bigFormat_format, iters) {
BenchmarkSuspender suspender;
char* p;
auto writeToBuf = [&p](StringPiece sp) mutable {
memcpy(p, sp.data(), sp.size());
p += sp.size();
};
while (iters--) {
for (int i = -100; i < 100; i++) {
p = bigBuf.data();
suspender.dismissing([&] {
format20Numbers(i, std::make_index_sequence<20>())(writeToBuf);
});
}
}
}
BENCHMARK_DRAW_LINE();
BENCHMARK(format_nested_strings, iters) {
BenchmarkSuspender suspender;
while (iters--) {
for (int i = 0; i < 1000; ++i) {
fbstring out;
suspender.dismissing([&] {
format(
&out,
"{} {}",
sformat("{} {}", i, i + 1),
sformat("{} {}", -i, -i - 1));
});
}
}
}
BENCHMARK_RELATIVE(format_nested_direct, iters) {
BenchmarkSuspender suspender;
while (iters--) {
for (int i = 0; i < 1000; ++i) {
fbstring out;
suspender.dismissing([&] {
format(
&out,
"{} {}",
format("{} {}", i, i + 1),
format("{} {}", -i, -i - 1));
});
}
}
}
BENCHMARK_DRAW_LINE();
BENCHMARK(copy_short_string, iters) {
BenchmarkSuspender suspender;
auto const& shortString = getShortString();
while (iters--) {
fbstring out;
suspender.dismissing([&] { out = shortString; });
}
}
BENCHMARK_RELATIVE(format_short_string_unsafe, iters) {
BenchmarkSuspender suspender;
auto const& shortString = getShortString();
while (iters--) {
fbstring out;
suspender.dismissing([&] { format(&out, shortString); });
}
}
BENCHMARK_RELATIVE(format_short_string_safe, iters) {
BenchmarkSuspender suspender;
auto const& shortString = getShortString();
while (iters--) {
fbstring out;
suspender.dismissing([&] { format(&out, "{}", shortString); });
}
}
BENCHMARK_RELATIVE(sformat_short_string_unsafe, iters) {
BenchmarkSuspender suspender;
auto const& shortString = getShortString();
while (iters--) {
std::string out;
suspender.dismissing([&] { out = sformat(shortString); });
}
}
BENCHMARK_RELATIVE(sformat_short_string_safe, iters) {
BenchmarkSuspender suspender;
auto const& shortString = getShortString();
while (iters--) {
std::string out;
suspender.dismissing([&] { out = sformat("{}", shortString); });
}
}
BENCHMARK_DRAW_LINE();
BENCHMARK(copy_long_string, iters) {
BenchmarkSuspender suspender;
auto const& longString = getLongString();
while (iters--) {
fbstring out;
suspender.dismissing([&] { out = longString; });
}
}
BENCHMARK_RELATIVE(format_long_string_unsafe, iters) {
BenchmarkSuspender suspender;
auto const& longString = getLongString();
while (iters--) {
fbstring out;
suspender.dismissing([&] { format(&out, longString); });
}
}
BENCHMARK_RELATIVE(format_long_string_safe, iters) {
BenchmarkSuspender suspender;
auto const& longString = getLongString();
while (iters--) {
fbstring out;
suspender.dismissing([&] { format(&out, "{}", longString); });
}
}
BENCHMARK_RELATIVE(sformat_long_string_unsafe, iters) {
BenchmarkSuspender suspender;
auto const& longString = getLongString();
while (iters--) {
std::string out;
suspender.dismissing([&] { out = sformat(longString); });
}
}
BENCHMARK_RELATIVE(sformat_long_string_safe, iters) {
BenchmarkSuspender suspender;
auto const& longString = getLongString();
while (iters--) {
std::string out;
suspender.dismissing([&] { out = sformat("{}", longString); });
}
}
// Benchmark results on my dev server (20-core Intel Xeon E5-2660 v2 @ 2.20GHz)
//
// ============================================================================
// folly/test/FormatBenchmark.cpp relative time/iter iters/s
// ============================================================================
// octal_snprintf 79.30ns 12.61M
// octal_uintToOctal 3452.19% 2.30ns 435.35M
// ----------------------------------------------------------------------------
// hex_snprintf 73.59ns 13.59M
// hex_uintToHex 4507.53% 1.63ns 612.49M
// ----------------------------------------------------------------------------
// intAppend_snprintf 191.50us 5.22K
// intAppend_to 552.46% 34.66us 28.85K
// intAppend_format 215.76% 88.76us 11.27K
// ----------------------------------------------------------------------------
// bigFormat_snprintf 178.03us 5.62K
// bigFormat_format 90.41% 196.91us 5.08K
// ----------------------------------------------------------------------------
// format_nested_strings 317.65us 3.15K
// format_nested_direct 116.52% 272.62us 3.67K
// ----------------------------------------------------------------------------
// copy_short_string 28.33ns 35.30M
// format_short_string_unsafe 82.51% 34.33ns 29.13M
// format_short_string_safe 58.92% 48.08ns 20.80M
// sformat_short_string_unsafe 73.90% 38.33ns 26.09M
// sformat_short_string_safe 54.97% 51.53ns 19.41M
// ----------------------------------------------------------------------------
// copy_long_string 57.56ns 17.37M
// format_long_string_unsafe 68.79% 83.68ns 11.95M
// format_long_string_safe 69.44% 82.89ns 12.06M
// sformat_long_string_unsafe 65.58% 87.77ns 11.39M
// sformat_long_string_safe 68.14% 84.47ns 11.84M
// ============================================================================
int main(int argc, char* argv[]) {
folly::Init init(&argc, &argv, true);
runBenchmarks();
return 0;
}