Commit f00612cf authored by Michael Lee's avatar Michael Lee Committed by facebook-github-bot-4

Move StringTest benchmarks into StringBenchmark.cpp

Summary: StringTest.cpp is a mix of benchmarks and normal unittests. Fixing this.

Reviewed By: yfeldblum

Differential Revision: D2840804

fb-gh-sync-id: d3efc357f5f09385e9f69b70e38b64d59045ff0d
parent edc4bd3f
......@@ -149,7 +149,7 @@ map_util_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
TESTS += map_util_test
string_test_SOURCES = StringTest.cpp
string_test_LDADD = libgtest.la $(top_builddir)/libfolly.la $(top_builddir)/libfollybenchmark.la
string_test_LDADD = libgtest.la $(top_builddir)/libfolly.la
TESTS += string_test
producer_consumer_queue_test_SOURCES = ProducerConsumerQueueTest.cpp
......
......@@ -14,9 +14,16 @@
* limitations under the License.
*/
#include <folly/Benchmark.h>
#include <folly/String.h>
#include <boost/algorithm/string.hpp>
#include <cstdarg>
#include <folly/Benchmark.h>
#include <random>
using namespace folly;
using namespace std;
BENCHMARK(libc_tolower, iters) {
static const size_t kSize = 256;
// This array is static to keep the compiler from optimizing the
......@@ -47,12 +54,210 @@ BENCHMARK(folly_toLowerAscii, iters) {
}
}
// A simple benchmark that tests various output sizes for a simple
// input; the goal is to measure the output buffer resize code cost.
void stringPrintfOutputSize(int iters, int param) {
string buffer;
BENCHMARK_SUSPEND { buffer.resize(param, 'x'); }
for (int64_t i = 0; i < iters; ++i) {
string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str());
}
}
// The first few of these tend to fit in the inline buffer, while the
// subsequent ones cross that limit, trigger a second vsnprintf, and
// exercise a different codepath.
BENCHMARK_PARAM(stringPrintfOutputSize, 1)
BENCHMARK_PARAM(stringPrintfOutputSize, 4)
BENCHMARK_PARAM(stringPrintfOutputSize, 16)
BENCHMARK_PARAM(stringPrintfOutputSize, 64)
BENCHMARK_PARAM(stringPrintfOutputSize, 256)
BENCHMARK_PARAM(stringPrintfOutputSize, 1024)
// Benchmark simple stringAppendf behavior to show a pathology Lovro
// reported (t5735468).
BENCHMARK(stringPrintfAppendfBenchmark, iters) {
for (unsigned int i = 0; i < iters; ++i) {
string s;
BENCHMARK_SUSPEND { s.reserve(300000); }
for (int j = 0; j < 300000; ++j) {
stringAppendf(&s, "%d", 1);
}
}
}
namespace {
fbstring cbmString;
fbstring cbmEscapedString;
fbstring cEscapedString;
fbstring cUnescapedString;
const size_t kCBmStringLength = 64 << 10;
const uint32_t kCPrintablePercentage = 90;
fbstring uribmString;
fbstring uribmEscapedString;
fbstring uriEscapedString;
fbstring uriUnescapedString;
const size_t kURIBmStringLength = 256;
const uint32_t kURIPassThroughPercentage = 50;
void initBenchmark() {
std::mt19937 rnd;
// C escape
std::uniform_int_distribution<uint32_t> printable(32, 126);
std::uniform_int_distribution<uint32_t> nonPrintable(0, 160);
std::uniform_int_distribution<uint32_t> percentage(0, 99);
cbmString.reserve(kCBmStringLength);
for (size_t i = 0; i < kCBmStringLength; ++i) {
unsigned char c;
if (percentage(rnd) < kCPrintablePercentage) {
c = printable(rnd);
} else {
c = nonPrintable(rnd);
// Generate characters in both non-printable ranges:
// 0..31 and 127..255
if (c >= 32) {
c += (126 - 32) + 1;
}
}
cbmString.push_back(c);
}
cbmEscapedString = cEscape<fbstring>(cbmString);
// URI escape
std::uniform_int_distribution<uint32_t> passthrough('a', 'z');
std::string encodeChars = " ?!\"',+[]";
std::uniform_int_distribution<uint32_t> encode(0, encodeChars.size() - 1);
uribmString.reserve(kURIBmStringLength);
for (size_t i = 0; i < kURIBmStringLength; ++i) {
unsigned char c;
if (percentage(rnd) < kURIPassThroughPercentage) {
c = passthrough(rnd);
} else {
c = encodeChars[encode(rnd)];
}
uribmString.push_back(c);
}
uribmEscapedString = uriEscape<fbstring>(uribmString);
}
BENCHMARK(BM_cEscape, iters) {
while (iters--) {
cEscapedString = cEscape<fbstring>(cbmString);
doNotOptimizeAway(cEscapedString.size());
}
}
BENCHMARK(BM_cUnescape, iters) {
while (iters--) {
cUnescapedString = cUnescape<fbstring>(cbmEscapedString);
doNotOptimizeAway(cUnescapedString.size());
}
}
BENCHMARK(BM_uriEscape, iters) {
while (iters--) {
uriEscapedString = uriEscape<fbstring>(uribmString);
doNotOptimizeAway(uriEscapedString.size());
}
}
BENCHMARK(BM_uriUnescape, iters) {
while (iters--) {
uriUnescapedString = uriUnescape<fbstring>(uribmEscapedString);
doNotOptimizeAway(uriUnescapedString.size());
}
}
} // namespace
//////////////////////////////////////////////////////////////////////
BENCHMARK(splitOnSingleChar, iters) {
static const std::string line = "one:two:three:four";
for (size_t i = 0; i < iters << 4; ++i) {
std::vector<StringPiece> pieces;
folly::split(':', line, pieces);
}
}
BENCHMARK(splitOnSingleCharFixed, iters) {
static const std::string line = "one:two:three:four";
for (size_t i = 0; i < iters << 4; ++i) {
StringPiece a, b, c, d;
folly::split(':', line, a, b, c, d);
}
}
BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) {
static const std::string line = "one:two:three:four";
for (size_t i = 0; i < iters << 4; ++i) {
StringPiece a, b, c, d;
folly::split<false>(':', line, a, b, c, d);
}
}
BENCHMARK(splitStr, iters) {
static const std::string line = "one-*-two-*-three-*-four";
for (size_t i = 0; i < iters << 4; ++i) {
std::vector<StringPiece> pieces;
folly::split("-*-", line, pieces);
}
}
BENCHMARK(splitStrFixed, iters) {
static const std::string line = "one-*-two-*-three-*-four";
for (size_t i = 0; i < iters << 4; ++i) {
StringPiece a, b, c, d;
folly::split("-*-", line, a, b, c, d);
}
}
BENCHMARK(boost_splitOnSingleChar, iters) {
static const std::string line = "one:two:three:four";
bool (*pred)(char) = [](char c) -> bool { return c == ':'; };
for (size_t i = 0; i < iters << 4; ++i) {
std::vector<boost::iterator_range<std::string::const_iterator>> pieces;
boost::split(pieces, line, pred);
}
}
BENCHMARK(joinCharStr, iters) {
static const std::vector<std::string> input = {
"one", "two", "three", "four", "five", "six", "seven"};
for (size_t i = 0; i < iters << 4; ++i) {
std::string output;
folly::join(':', input, output);
}
}
BENCHMARK(joinStrStr, iters) {
static const std::vector<std::string> input = {
"one", "two", "three", "four", "five", "six", "seven"};
for (size_t i = 0; i < iters << 4; ++i) {
std::string output;
folly::join(":", input, output);
}
}
BENCHMARK(joinInt, iters) {
static const auto input = {123, 456, 78910, 1112, 1314, 151, 61718};
for (size_t i = 0; i < iters << 4; ++i) {
std::string output;
folly::join(":", input, output);
}
}
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
initBenchmark();
folly::runBenchmarks();
if (FLAGS_benchmark) {
folly::runBenchmarks();
}
return 0;
}
......@@ -61,7 +266,27 @@ Results on x86_64:
============================================================================
folly/test/StringBenchmark.cpp relative time/iter iters/s
============================================================================
libc_tolower 1.30us 767.50K
folly_toLowerAscii 115.21ns 8.68M
libc_tolower 773.30ns 1.29M
folly_toLowerAscii 65.04ns 15.38M
stringPrintfOutputSize(1) 224.67ns 4.45M
stringPrintfOutputSize(4) 231.53ns 4.32M
stringPrintfOutputSize(16) 286.54ns 3.49M
stringPrintfOutputSize(64) 305.47ns 3.27M
stringPrintfOutputSize(256) 1.48us 674.45K
stringPrintfOutputSize(1024) 5.89us 169.72K
stringPrintfAppendfBenchmark 34.43ms 29.04
BM_cEscape 461.51us 2.17K
BM_cUnescape 328.19us 3.05K
BM_uriEscape 4.36us 229.25K
BM_uriUnescape 2.22us 450.64K
splitOnSingleChar 1.46us 687.21K
splitOnSingleCharFixed 133.02ns 7.52M
splitOnSingleCharFixedAllowExtra 74.35ns 13.45M
splitStr 2.36us 424.00K
splitStrFixed 282.38ns 3.54M
boost_splitOnSingleChar 2.83us 353.12K
joinCharStr 2.65us 376.93K
joinStrStr 2.64us 378.09K
joinInt 3.89us 257.37K
============================================================================
*/
......@@ -16,13 +16,8 @@
#include <folly/String.h>
#include <cstdarg>
#include <random>
#include <boost/algorithm/string.hpp>
#include <gtest/gtest.h>
#include <folly/Benchmark.h>
using namespace folly;
using namespace std;
......@@ -155,39 +150,6 @@ TEST(StringPrintf, oldStringAppendf) {
EXPECT_EQ(string("helloa/b/c/d"), s);
}
// A simple benchmark that tests various output sizes for a simple
// input; the goal is to measure the output buffer resize code cost.
void stringPrintfOutputSize(int iters, int param) {
string buffer;
BENCHMARK_SUSPEND { buffer.resize(param, 'x'); }
for (int64_t i = 0; i < iters; ++i) {
string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str());
}
}
// The first few of these tend to fit in the inline buffer, while the
// subsequent ones cross that limit, trigger a second vsnprintf, and
// exercise a different codepath.
BENCHMARK_PARAM(stringPrintfOutputSize, 1)
BENCHMARK_PARAM(stringPrintfOutputSize, 4)
BENCHMARK_PARAM(stringPrintfOutputSize, 16)
BENCHMARK_PARAM(stringPrintfOutputSize, 64)
BENCHMARK_PARAM(stringPrintfOutputSize, 256)
BENCHMARK_PARAM(stringPrintfOutputSize, 1024)
// Benchmark simple stringAppendf behavior to show a pathology Lovro
// reported (t5735468).
BENCHMARK(stringPrintfAppendfBenchmark, iters) {
for (unsigned int i = 0; i < iters; ++i) {
string s;
BENCHMARK_SUSPEND { s.reserve(300000); }
for (int j = 0; j < 300000; ++j) {
stringAppendf(&s, "%d", 1);
}
}
}
TEST(Escape, cEscape) {
EXPECT_EQ("hello world", cEscape<std::string>("hello world"));
EXPECT_EQ("hello \\\\world\\\" goodbye",
......@@ -303,96 +265,6 @@ TEST(Escape, uriUnescapePercentDecoding) {
}
}
namespace {
fbstring cbmString;
fbstring cbmEscapedString;
fbstring cEscapedString;
fbstring cUnescapedString;
const size_t kCBmStringLength = 64 << 10;
const uint32_t kCPrintablePercentage = 90;
fbstring uribmString;
fbstring uribmEscapedString;
fbstring uriEscapedString;
fbstring uriUnescapedString;
const size_t kURIBmStringLength = 256;
const uint32_t kURIPassThroughPercentage = 50;
void initBenchmark() {
std::mt19937 rnd;
// C escape
std::uniform_int_distribution<uint32_t> printable(32, 126);
std::uniform_int_distribution<uint32_t> nonPrintable(0, 160);
std::uniform_int_distribution<uint32_t> percentage(0, 99);
cbmString.reserve(kCBmStringLength);
for (size_t i = 0; i < kCBmStringLength; ++i) {
unsigned char c;
if (percentage(rnd) < kCPrintablePercentage) {
c = printable(rnd);
} else {
c = nonPrintable(rnd);
// Generate characters in both non-printable ranges:
// 0..31 and 127..255
if (c >= 32) {
c += (126 - 32) + 1;
}
}
cbmString.push_back(c);
}
cbmEscapedString = cEscape<fbstring>(cbmString);
// URI escape
std::uniform_int_distribution<uint32_t> passthrough('a', 'z');
std::string encodeChars = " ?!\"',+[]";
std::uniform_int_distribution<uint32_t> encode(0, encodeChars.size() - 1);
uribmString.reserve(kURIBmStringLength);
for (size_t i = 0; i < kURIBmStringLength; ++i) {
unsigned char c;
if (percentage(rnd) < kURIPassThroughPercentage) {
c = passthrough(rnd);
} else {
c = encodeChars[encode(rnd)];
}
uribmString.push_back(c);
}
uribmEscapedString = uriEscape<fbstring>(uribmString);
}
BENCHMARK(BM_cEscape, iters) {
while (iters--) {
cEscapedString = cEscape<fbstring>(cbmString);
doNotOptimizeAway(cEscapedString.size());
}
}
BENCHMARK(BM_cUnescape, iters) {
while (iters--) {
cUnescapedString = cUnescape<fbstring>(cbmEscapedString);
doNotOptimizeAway(cUnescapedString.size());
}
}
BENCHMARK(BM_uriEscape, iters) {
while (iters--) {
uriEscapedString = uriEscape<fbstring>(uribmString);
doNotOptimizeAway(uriEscapedString.size());
}
}
BENCHMARK(BM_uriUnescape, iters) {
while (iters--) {
uriUnescapedString = uriUnescape<fbstring>(uribmEscapedString);
doNotOptimizeAway(uriUnescapedString.size());
}
}
} // namespace
namespace {
double pow2(int exponent) {
......@@ -1212,84 +1084,6 @@ TEST(String, toLowerAsciiUnaligned) {
}
}
//////////////////////////////////////////////////////////////////////
BENCHMARK(splitOnSingleChar, iters) {
static const std::string line = "one:two:three:four";
for (size_t i = 0; i < iters << 4; ++i) {
std::vector<StringPiece> pieces;
folly::split(':', line, pieces);
}
}
BENCHMARK(splitOnSingleCharFixed, iters) {
static const std::string line = "one:two:three:four";
for (size_t i = 0; i < iters << 4; ++i) {
StringPiece a, b, c, d;
folly::split(':', line, a, b, c, d);
}
}
BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) {
static const std::string line = "one:two:three:four";
for (size_t i = 0; i < iters << 4; ++i) {
StringPiece a, b, c, d;
folly::split<false>(':', line, a, b, c, d);
}
}
BENCHMARK(splitStr, iters) {
static const std::string line = "one-*-two-*-three-*-four";
for (size_t i = 0; i < iters << 4; ++i) {
std::vector<StringPiece> pieces;
folly::split("-*-", line, pieces);
}
}
BENCHMARK(splitStrFixed, iters) {
static const std::string line = "one-*-two-*-three-*-four";
for (size_t i = 0; i < iters << 4; ++i) {
StringPiece a, b, c, d;
folly::split("-*-", line, a, b, c, d);
}
}
BENCHMARK(boost_splitOnSingleChar, iters) {
static const std::string line = "one:two:three:four";
bool(*pred)(char) = [] (char c) -> bool { return c == ':'; };
for (size_t i = 0; i < iters << 4; ++i) {
std::vector<boost::iterator_range<std::string::const_iterator> > pieces;
boost::split(pieces, line, pred);
}
}
BENCHMARK(joinCharStr, iters) {
static const std::vector<std::string> input = {
"one", "two", "three", "four", "five", "six", "seven" };
for (size_t i = 0; i < iters << 4; ++i) {
std::string output;
folly::join(':', input, output);
}
}
BENCHMARK(joinStrStr, iters) {
static const std::vector<std::string> input = {
"one", "two", "three", "four", "five", "six", "seven" };
for (size_t i = 0; i < iters << 4; ++i) {
std::string output;
folly::join(":", input, output);
}
}
BENCHMARK(joinInt, iters) {
static const auto input = {
123, 456, 78910, 1112, 1314, 151, 61718 };
for (size_t i = 0; i < iters << 4; ++i) {
std::string output;
folly::join(":", input, output);
}
}
TEST(String, whitespace) {
// trimWhitespace:
EXPECT_EQ("kavabanga",
......@@ -1359,16 +1153,3 @@ TEST(UTF8StringPiece, empty_mid_codepoint) {
TEST(UTF8StringPiece, invalid_mid_codepoint) {
EXPECT_THROW(UTF8StringPiece(kTestUTF8.subpiece(9, 1)), std::out_of_range);
}
int main(int argc, char *argv[]) {
testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
auto ret = RUN_ALL_TESTS();
if (!ret) {
initBenchmark();
if (FLAGS_benchmark) {
folly::runBenchmarks();
}
}
return ret;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment