Commit eb46a63d by Gennadiy Rozental Committed by Copybara-Service

Optimize the absl::GetFlag cost for most non built-in flag types (including string).

PiperOrigin-RevId: 649200175
Change-Id: Ic6741d9fe5e0b1853ed8bb37b585d38b51d15581
parent 6e701508
...@@ -390,6 +390,7 @@ cc_test( ...@@ -390,6 +390,7 @@ cc_test(
":flag", ":flag",
":flag_internal", ":flag_internal",
":marshalling", ":marshalling",
":parse",
":reflection", ":reflection",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:malloc_internal", "//absl/base:malloc_internal",
......
...@@ -340,6 +340,7 @@ absl_cc_test( ...@@ -340,6 +340,7 @@ absl_cc_test(
absl::flags_config absl::flags_config
absl::flags_internal absl::flags_internal
absl::flags_marshalling absl::flags_marshalling
absl::flags_parse
absl::flags_reflection absl::flags_reflection
absl::int128 absl::int128
absl::optional absl::optional
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/flags/internal/flag.h" #include "absl/flags/internal/flag.h"
#include "absl/flags/marshalling.h" #include "absl/flags/marshalling.h"
#include "absl/flags/parse.h"
#include "absl/flags/reflection.h" #include "absl/flags/reflection.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/numeric/int128.h" #include "absl/numeric/int128.h"
...@@ -126,9 +127,9 @@ TEST_F(FlagTest, Traits) { ...@@ -126,9 +127,9 @@ TEST_F(FlagTest, Traits) {
#endif #endif
EXPECT_EQ(flags::StorageKind<std::string>(), EXPECT_EQ(flags::StorageKind<std::string>(),
flags::FlagValueStorageKind::kAlignedBuffer); flags::FlagValueStorageKind::kHeapAllocated);
EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(), EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(),
flags::FlagValueStorageKind::kAlignedBuffer); flags::FlagValueStorageKind::kHeapAllocated);
EXPECT_EQ(flags::StorageKind<absl::int128>(), EXPECT_EQ(flags::StorageKind<absl::int128>(),
flags::FlagValueStorageKind::kSequenceLocked); flags::FlagValueStorageKind::kSequenceLocked);
...@@ -267,7 +268,7 @@ TEST_F(FlagTest, TestFlagDeclaration) { ...@@ -267,7 +268,7 @@ TEST_F(FlagTest, TestFlagDeclaration) {
#if ABSL_FLAGS_STRIP_NAMES #if ABSL_FLAGS_STRIP_NAMES
// The intent of this helper struct and an expression below is to make sure that // The intent of this helper struct and an expression below is to make sure that
// in the configuration where ABSL_FLAGS_STRIP_NAMES=1 registrar construction // in the configuration where ABSL_FLAGS_STRIP_NAMES=1 registrar construction
// (in cases of of no Tail calls like OnUpdate) is constexpr and thus can and // (in cases of no Tail calls like OnUpdate) is constexpr and thus can and
// should be completely optimized away, thus avoiding the cost/overhead of // should be completely optimized away, thus avoiding the cost/overhead of
// static initializers. // static initializers.
struct VerifyConsteval { struct VerifyConsteval {
...@@ -515,8 +516,10 @@ TEST_F(FlagTest, TestDefault) { ...@@ -515,8 +516,10 @@ TEST_F(FlagTest, TestDefault) {
struct NonTriviallyCopyableAggregate { struct NonTriviallyCopyableAggregate {
NonTriviallyCopyableAggregate() = default; NonTriviallyCopyableAggregate() = default;
// NOLINTNEXTLINE
NonTriviallyCopyableAggregate(const NonTriviallyCopyableAggregate& rhs) NonTriviallyCopyableAggregate(const NonTriviallyCopyableAggregate& rhs)
: value(rhs.value) {} : value(rhs.value) {}
// NOLINTNEXTLINE
NonTriviallyCopyableAggregate& operator=( NonTriviallyCopyableAggregate& operator=(
const NonTriviallyCopyableAggregate& rhs) { const NonTriviallyCopyableAggregate& rhs) {
value = rhs.value; value = rhs.value;
...@@ -975,51 +978,194 @@ bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) { ...@@ -975,51 +978,194 @@ bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) {
} }
std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; } std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; }
// User-defined type with small size, but not trivially copyable. } // namespace
ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help");
namespace {
TEST_F(FlagTest, TestSmallAlignUDT) {
EXPECT_EQ(flags::StorageKind<SmallAlignUDT>(),
flags::FlagValueStorageKind::kSequenceLocked);
SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt);
EXPECT_EQ(value.c, 'A');
EXPECT_EQ(value.s, 12);
value.c = 'B';
value.s = 45;
absl::SetFlag(&FLAGS_test_flag_sa_udt, value);
value = absl::GetFlag(FLAGS_test_flag_sa_udt);
EXPECT_EQ(value.c, 'B');
EXPECT_EQ(value.s, 45);
}
} // namespace
// --------------------------------------------------------------------
namespace {
// User-defined not trivially copyable type.
template <int id>
struct NonTriviallyCopyableUDT { struct NonTriviallyCopyableUDT {
NonTriviallyCopyableUDT() : c('A') {} NonTriviallyCopyableUDT() : c('A') { s_num_instance++; }
NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {} NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {
s_num_instance++;
}
NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) { NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) {
c = rhs.c; c = rhs.c;
return *this; return *this;
} }
~NonTriviallyCopyableUDT() { s_num_instance--; }
static uint64_t s_num_instance;
char c; char c;
}; };
bool AbslParseFlag(absl::string_view, NonTriviallyCopyableUDT*, std::string*) { template <int id>
uint64_t NonTriviallyCopyableUDT<id>::s_num_instance = 0;
template <int id>
bool AbslParseFlag(absl::string_view txt, NonTriviallyCopyableUDT<id>* f,
std::string*) {
f->c = txt.empty() ? '\0' : txt[0];
return true; return true;
} }
std::string AbslUnparseFlag(const NonTriviallyCopyableUDT&) { return ""; } template <int id>
std::string AbslUnparseFlag(const NonTriviallyCopyableUDT<id>&) {
return "";
}
template <int id, typename F>
void TestExpectedLeaks(
F&& f, uint64_t num_leaks,
absl::optional<uint64_t> num_new_instances = absl::nullopt) {
if (!num_new_instances.has_value()) num_new_instances = num_leaks;
auto num_leaked_before = flags::NumLeakedFlagValues();
auto num_instances_before = NonTriviallyCopyableUDT<id>::s_num_instance;
f();
EXPECT_EQ(num_leaked_before + num_leaks, flags::NumLeakedFlagValues());
EXPECT_EQ(num_instances_before + num_new_instances.value(),
NonTriviallyCopyableUDT<id>::s_num_instance);
}
} // namespace } // namespace
ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help"); ABSL_FLAG(NonTriviallyCopyableUDT<1>, test_flag_ntc_udt1, {}, "help");
ABSL_FLAG(NonTriviallyCopyableUDT, test_flag_ntc_udt, {}, "help"); ABSL_FLAG(NonTriviallyCopyableUDT<2>, test_flag_ntc_udt2, {}, "help");
ABSL_FLAG(NonTriviallyCopyableUDT<3>, test_flag_ntc_udt3, {}, "help");
ABSL_FLAG(NonTriviallyCopyableUDT<4>, test_flag_ntc_udt4, {}, "help");
ABSL_FLAG(NonTriviallyCopyableUDT<5>, test_flag_ntc_udt5, {}, "help");
namespace { namespace {
TEST_F(FlagTest, TestSmallAlignUDT) { TEST_F(FlagTest, TestNonTriviallyCopyableGetSetSet) {
SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt); EXPECT_EQ(flags::StorageKind<NonTriviallyCopyableUDT<1>>(),
EXPECT_EQ(value.c, 'A'); flags::FlagValueStorageKind::kHeapAllocated);
EXPECT_EQ(value.s, 12);
TestExpectedLeaks<1>(
[&] {
NonTriviallyCopyableUDT<1> value =
absl::GetFlag(FLAGS_test_flag_ntc_udt1);
EXPECT_EQ(value.c, 'A');
},
0);
TestExpectedLeaks<1>(
[&] {
NonTriviallyCopyableUDT<1> value;
value.c = 'B';
absl::SetFlag(&FLAGS_test_flag_ntc_udt1, value);
EXPECT_EQ(value.c, 'B');
},
1);
TestExpectedLeaks<1>(
[&] {
NonTriviallyCopyableUDT<1> value;
value.c = 'C';
absl::SetFlag(&FLAGS_test_flag_ntc_udt1, value);
},
0);
}
value.c = 'B'; TEST_F(FlagTest, TestNonTriviallyCopyableParseSet) {
value.s = 45; TestExpectedLeaks<2>(
absl::SetFlag(&FLAGS_test_flag_sa_udt, value); [&] {
value = absl::GetFlag(FLAGS_test_flag_sa_udt); const char* in_argv[] = {"testbin", "--test_flag_ntc_udt2=A"};
EXPECT_EQ(value.c, 'B'); absl::ParseCommandLine(2, const_cast<char**>(in_argv));
EXPECT_EQ(value.s, 45); },
0);
TestExpectedLeaks<2>(
[&] {
NonTriviallyCopyableUDT<2> value;
value.c = 'B';
absl::SetFlag(&FLAGS_test_flag_ntc_udt2, value);
EXPECT_EQ(value.c, 'B');
},
0);
} }
TEST_F(FlagTest, TestNonTriviallyCopyableUDT) { TEST_F(FlagTest, TestNonTriviallyCopyableSet) {
NonTriviallyCopyableUDT value = absl::GetFlag(FLAGS_test_flag_ntc_udt); TestExpectedLeaks<3>(
EXPECT_EQ(value.c, 'A'); [&] {
NonTriviallyCopyableUDT<3> value;
value.c = 'B';
absl::SetFlag(&FLAGS_test_flag_ntc_udt3, value);
EXPECT_EQ(value.c, 'B');
},
0);
}
value.c = 'B'; // One new instance created during initialization and stored in the flag.
absl::SetFlag(&FLAGS_test_flag_ntc_udt, value); auto premain_utd4_get =
value = absl::GetFlag(FLAGS_test_flag_ntc_udt); (TestExpectedLeaks<4>([] { (void)absl::GetFlag(FLAGS_test_flag_ntc_udt4); },
EXPECT_EQ(value.c, 'B'); 0, 1),
false);
TEST_F(FlagTest, TestNonTriviallyCopyableGetBeforeMainParseGet) {
TestExpectedLeaks<4>(
[&] {
const char* in_argv[] = {"testbin", "--test_flag_ntc_udt4=C"};
absl::ParseCommandLine(2, const_cast<char**>(in_argv));
},
1);
TestExpectedLeaks<4>(
[&] {
NonTriviallyCopyableUDT<4> value =
absl::GetFlag(FLAGS_test_flag_ntc_udt4);
EXPECT_EQ(value.c, 'C');
},
0);
}
// One new instance created during initialization, which is reused since it was
// never read.
auto premain_utd5_set = (TestExpectedLeaks<5>(
[] {
NonTriviallyCopyableUDT<5> value;
value.c = 'B';
absl::SetFlag(&FLAGS_test_flag_ntc_udt5, value);
},
0, 1),
false);
TEST_F(FlagTest, TestNonTriviallyCopyableSetParseGet) {
TestExpectedLeaks<5>(
[&] {
const char* in_argv[] = {"testbin", "--test_flag_ntc_udt5=C"};
absl::ParseCommandLine(2, const_cast<char**>(in_argv));
},
0);
TestExpectedLeaks<5>(
[&] {
NonTriviallyCopyableUDT<5> value =
absl::GetFlag(FLAGS_test_flag_ntc_udt5);
EXPECT_EQ(value.c, 'C');
},
0);
} }
} // namespace } // namespace
......
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