summaryrefslogtreecommitdiffstats
path: root/googletest/src
diff options
context:
space:
mode:
authorAbseil Team <absl-team@google.com>2024-03-05 10:36:18 (GMT)
committerCopybara-Service <copybara-worker@google.com>2024-03-05 10:37:09 (GMT)
commitb9059aaa4cb93b3fb177670cbfd9c6d0690af37e (patch)
tree4e7561407a284cb66b326fb0a40b5ac0ec13c65d /googletest/src
parent926f681a902cbfb84a863dafb77175f4a25be6ea (diff)
downloadgoogletest-b9059aaa4cb93b3fb177670cbfd9c6d0690af37e.zip
googletest-b9059aaa4cb93b3fb177670cbfd9c6d0690af37e.tar.gz
googletest-b9059aaa4cb93b3fb177670cbfd9c6d0690af37e.tar.bz2
Optimize Google Test process startup
Google Test performs hidden test registration during process startup. For test binaries that contain a large number of tests, this registration can be costly. In this CL, we reduce the overhead of registration via several tactics: - Treat CodeLocation and FilePath as value types, using std::move to pass them around. - Reduce string copies in various places by either passing std::string values via std::move, or passing const-refs to std::string instances. - Use std::to_string to stringify an int in DefaultParamName rather than a std::stringstream. - Pull some std::string instances out of nested loops in ParameterizedTestSuiteInfo::RegisterTests so as to reuse some allocations, and replace stringstream with ordinary string appends. - Use std::unordered_map in UnitTestImpl::GetTestSuite and ParameterizedTestSuiteRegistry::GetTestSuitePatternHolder to spend a little memory to turn O(N) lookups into constant time lookpus. - Use range-based for loops in various places. - Use emplace-ish methods to add to containers where appropriate. All together, these changes reduce the overall runtime of a series of 50 death tests in a single Chromium test executable by ~38% due to the fact that the registration costs are paid in every death test's child process. PiperOrigin-RevId: 612763676 Change-Id: I1f46e012ccb9004c009e1027e4f7c38780ffb9e2
Diffstat (limited to 'googletest/src')
-rw-r--r--googletest/src/gtest-internal-inl.h20
-rw-r--r--googletest/src/gtest.cc117
2 files changed, 66 insertions, 71 deletions
diff --git a/googletest/src/gtest-internal-inl.h b/googletest/src/gtest-internal-inl.h
index 6dea34f..6a7f4dd 100644
--- a/googletest/src/gtest-internal-inl.h
+++ b/googletest/src/gtest-internal-inl.h
@@ -46,6 +46,7 @@
#include <memory>
#include <set>
#include <string>
+#include <unordered_map>
#include <vector>
#include "gtest/internal/gtest-port.h"
@@ -649,13 +650,15 @@ class GTEST_API_ UnitTestImpl {
// this is not a typed or a type-parameterized test.
// set_up_tc: pointer to the function that sets up the test suite
// tear_down_tc: pointer to the function that tears down the test suite
- TestSuite* GetTestSuite(const char* test_suite_name, const char* type_param,
+ TestSuite* GetTestSuite(const std::string& test_suite_name,
+ const char* type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc);
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
- TestCase* GetTestCase(const char* test_case_name, const char* type_param,
+ TestCase* GetTestCase(const std::string& test_case_name,
+ const char* type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc) {
return GetTestSuite(test_case_name, type_param, set_up_tc, tear_down_tc);
@@ -681,13 +684,13 @@ class GTEST_API_ UnitTestImpl {
// AddTestInfo(), which is called to register a TEST or TEST_F
// before main() is reached.
if (original_working_dir_.IsEmpty()) {
- original_working_dir_.Set(FilePath::GetCurrentDir());
+ original_working_dir_ = FilePath::GetCurrentDir();
GTEST_CHECK_(!original_working_dir_.IsEmpty())
<< "Failed to get the current working directory.";
}
#endif // GTEST_HAS_FILE_SYSTEM
- GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
+ GetTestSuite(test_info->test_suite_name_, test_info->type_param(),
set_up_tc, tear_down_tc)
->AddTestInfo(test_info);
}
@@ -823,6 +826,12 @@ class GTEST_API_ UnitTestImpl {
bool catch_exceptions() const { return catch_exceptions_; }
private:
+ struct CompareTestSuitesByPointer {
+ bool operator()(const TestSuite* lhs, const TestSuite* rhs) const {
+ return lhs->name_ < rhs->name_;
+ };
+ };
+
friend class ::testing::UnitTest;
// Used by UnitTest::Run() to capture the state of
@@ -873,6 +882,9 @@ class GTEST_API_ UnitTestImpl {
// elements in the vector.
std::vector<TestSuite*> test_suites_;
+ // The set of TestSuites by name.
+ std::unordered_map<std::string, TestSuite*> test_suites_by_name_;
+
// Provides a level of indirection for the test suite list to allow
// easy shuffling and restoring the test suite order. The i-th
// element of this vector is the index of the i-th test suite in the
diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc
index c5f22bb..ca9f524 100644
--- a/googletest/src/gtest.cc
+++ b/googletest/src/gtest.cc
@@ -578,7 +578,7 @@ void InsertSyntheticTestCase(const std::string& name, CodeLocation location,
void RegisterTypeParameterizedTestSuite(const char* test_suite_name,
CodeLocation code_location) {
GetUnitTestImpl()->type_parameterized_test_registry().RegisterTestSuite(
- test_suite_name, code_location);
+ test_suite_name, std::move(code_location));
}
void RegisterTypeParameterizedTestSuiteInstantiation(const char* case_name) {
@@ -589,7 +589,7 @@ void RegisterTypeParameterizedTestSuiteInstantiation(const char* case_name) {
void TypeParameterizedTestSuiteRegistry::RegisterTestSuite(
const char* test_suite_name, CodeLocation code_location) {
suites_.emplace(std::string(test_suite_name),
- TypeParameterizedTestSuiteInfo(code_location));
+ TypeParameterizedTestSuiteInfo(std::move(code_location)));
}
void TypeParameterizedTestSuiteRegistry::RegisterInstantiation(
@@ -801,7 +801,7 @@ class UnitTestFilter {
// Returns true if and only if name matches at least one of the patterns in
// the filter.
bool MatchesName(const std::string& name) const {
- return exact_match_patterns_.count(name) > 0 ||
+ return exact_match_patterns_.find(name) != exact_match_patterns_.end() ||
std::any_of(glob_patterns_.begin(), glob_patterns_.end(),
[&name](const std::string& pattern) {
return PatternMatchesString(
@@ -2740,18 +2740,16 @@ bool Test::IsSkipped() {
// Constructs a TestInfo object. It assumes ownership of the test factory
// object.
-TestInfo::TestInfo(const std::string& a_test_suite_name,
- const std::string& a_name, const char* a_type_param,
- const char* a_value_param,
+TestInfo::TestInfo(std::string a_test_suite_name, std::string a_name,
+ const char* a_type_param, const char* a_value_param,
internal::CodeLocation a_code_location,
internal::TypeId fixture_class_id,
internal::TestFactoryBase* factory)
- : test_suite_name_(a_test_suite_name),
- // begin()/end() is MSVC 17.3.3 ASAN crash workaround (GitHub issue #3997)
- name_(a_name.begin(), a_name.end()),
+ : test_suite_name_(std::move(a_test_suite_name)),
+ name_(std::move(a_name)),
type_param_(a_type_param ? new std::string(a_type_param) : nullptr),
value_param_(a_value_param ? new std::string(a_value_param) : nullptr),
- location_(a_code_location),
+ location_(std::move(a_code_location)),
fixture_class_id_(fixture_class_id),
should_run_(false),
is_disabled_(false),
@@ -2784,19 +2782,19 @@ namespace internal {
// The newly created TestInfo instance will assume
// ownership of the factory object.
TestInfo* MakeAndRegisterTestInfo(
- const char* test_suite_name, const char* name, const char* type_param,
+ std::string test_suite_name, const char* name, const char* type_param,
const char* value_param, CodeLocation code_location,
TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {
TestInfo* const test_info =
- new TestInfo(test_suite_name, name, type_param, value_param,
- code_location, fixture_class_id, factory);
+ new TestInfo(std::move(test_suite_name), name, type_param, value_param,
+ std::move(code_location), fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
return test_info;
}
void ReportInvalidTestSuiteType(const char* test_suite_name,
- CodeLocation code_location) {
+ const CodeLocation& code_location) {
Message errors;
errors
<< "Attempted redefinition of test suite " << test_suite_name << ".\n"
@@ -2948,7 +2946,7 @@ int TestSuite::total_test_count() const {
// this is not a typed or a type-parameterized test suite.
// set_up_tc: pointer to the function that sets up the test suite
// tear_down_tc: pointer to the function that tears down the test suite
-TestSuite::TestSuite(const char* a_name, const char* a_type_param,
+TestSuite::TestSuite(const std::string& a_name, const char* a_type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc)
: name_(a_name),
@@ -3836,11 +3834,10 @@ void TestEventRepeater::Append(TestEventListener* listener) {
}
TestEventListener* TestEventRepeater::Release(TestEventListener* listener) {
- for (size_t i = 0; i < listeners_.size(); ++i) {
- if (listeners_[i] == listener) {
- listeners_.erase(listeners_.begin() + static_cast<int>(i));
- return listener;
- }
+ auto iter = std::find(listeners_.begin(), listeners_.end(), listener);
+ if (iter != listeners_.end()) {
+ listeners_.erase(iter);
+ return listener;
}
return nullptr;
@@ -3851,20 +3848,21 @@ TestEventListener* TestEventRepeater::Release(TestEventListener* listener) {
#define GTEST_REPEATER_METHOD_(Name, Type) \
void TestEventRepeater::Name(const Type& parameter) { \
if (forwarding_enabled_) { \
- for (size_t i = 0; i < listeners_.size(); i++) { \
- listeners_[i]->Name(parameter); \
+ for (auto* listener : listeners_) { \
+ listener->Name(parameter); \
} \
} \
}
// This defines a member that forwards the call to all listeners in reverse
// order.
-#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
- void TestEventRepeater::Name(const Type& parameter) { \
- if (forwarding_enabled_) { \
- for (size_t i = listeners_.size(); i != 0; i--) { \
- listeners_[i - 1]->Name(parameter); \
- } \
- } \
+#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
+ void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ const auto end = listeners_.rend(); \
+ for (auto scan = listeners_.rbegin(); scan != end; ++scan) { \
+ (*scan)->Name(parameter); \
+ } \
+ } \
}
GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest)
@@ -3894,8 +3892,8 @@ GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest)
void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test,
int iteration) {
if (forwarding_enabled_) {
- for (size_t i = 0; i < listeners_.size(); i++) {
- listeners_[i]->OnTestIterationStart(unit_test, iteration);
+ for (auto* listener : listeners_) {
+ listener->OnTestIterationStart(unit_test, iteration);
}
}
}
@@ -3903,8 +3901,9 @@ void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test,
void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test,
int iteration) {
if (forwarding_enabled_) {
- for (size_t i = listeners_.size(); i > 0; i--) {
- listeners_[i - 1]->OnTestIterationEnd(unit_test, iteration);
+ const auto end = listeners_.rend();
+ for (auto scan = listeners_.rbegin(); scan != end; ++scan) {
+ (*scan)->OnTestIterationEnd(unit_test, iteration);
}
}
}
@@ -5746,29 +5745,6 @@ void UnitTestImpl::PostFlagParsingInit() {
}
}
-// A predicate that checks the name of a TestSuite against a known
-// value.
-//
-// This is used for implementation of the UnitTest class only. We put
-// it in the anonymous namespace to prevent polluting the outer
-// namespace.
-//
-// TestSuiteNameIs is copyable.
-class TestSuiteNameIs {
- public:
- // Constructor.
- explicit TestSuiteNameIs(const std::string& name) : name_(name) {}
-
- // Returns true if and only if the name of test_suite matches name_.
- bool operator()(const TestSuite* test_suite) const {
- return test_suite != nullptr &&
- strcmp(test_suite->name(), name_.c_str()) == 0;
- }
-
- private:
- std::string name_;
-};
-
// Finds and returns a TestSuite with the given name. If one doesn't
// exist, creates one and returns it. It's the CALLER'S
// RESPONSIBILITY to ensure that this function is only called WHEN THE
@@ -5782,19 +5758,27 @@ class TestSuiteNameIs {
// set_up_tc: pointer to the function that sets up the test suite
// tear_down_tc: pointer to the function that tears down the test suite
TestSuite* UnitTestImpl::GetTestSuite(
- const char* test_suite_name, const char* type_param,
+ const std::string& test_suite_name, const char* type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc) {
- // Can we find a TestSuite with the given name?
- const auto test_suite =
- std::find_if(test_suites_.rbegin(), test_suites_.rend(),
- TestSuiteNameIs(test_suite_name));
+ // During initialization, all TestInfos for a given suite are added in
+ // sequence. To optimize this case, see if the most recently added suite is
+ // the one being requested now.
+ if (!test_suites_.empty() &&
+ (*test_suites_.rbegin())->name_ == test_suite_name) {
+ return *test_suites_.rbegin();
+ }
- if (test_suite != test_suites_.rend()) return *test_suite;
+ // Fall back to searching the collection.
+ auto item_it = test_suites_by_name_.find(test_suite_name);
+ if (item_it != test_suites_by_name_.end()) {
+ return item_it->second;
+ }
- // No. Let's create one.
+ // Not found. Create a new instance.
auto* const new_test_suite =
new TestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc);
+ test_suites_by_name_.emplace(test_suite_name, new_test_suite);
const UnitTestFilter death_test_suite_filter(kDeathTestSuiteFilter);
// Is this a death test suite?
@@ -6146,12 +6130,11 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
int num_runnable_tests = 0;
int num_selected_tests = 0;
for (auto* test_suite : test_suites_) {
- const std::string& test_suite_name = test_suite->name();
+ const std::string& test_suite_name = test_suite->name_;
test_suite->set_should_run(false);
- for (size_t j = 0; j < test_suite->test_info_list().size(); j++) {
- TestInfo* const test_info = test_suite->test_info_list()[j];
- const std::string test_name(test_info->name());
+ for (TestInfo* test_info : test_suite->test_info_list()) {
+ const std::string& test_name = test_info->name_;
// A test is disabled if test suite name or test name matches
// kDisableTestFilter.
const bool is_disabled =