Files
carbon-lang/toolchain/base/value_store_test.cpp
T
Chandler Carruth 44c85e0872 Reserve memory for the identifiers hashtable. (#4107)
This uses a heuristic reserve to greatly reduce hashtable growth of the
identifiers hashtable. The design of the hashtable itself is optimized
around compact memory use and is especially slow to grow and so this has
an outsized impact.

The heuristic was computed using `scripts/source_stats.py` and looking
at C++ codebases. We may want to periodically re-evaluate it as Carbon
code emerges and we have better data on its distributions of tokens.

This also required fixing the `Reserve` method on `CanonicalValueStore`
that wasn't actually used anywhere and so didn't even compile correctly.
I added it to the relevant unit test so it is at least compiled locally
to its definition.
2024-07-18 15:41:21 +00:00

164 lines
5.4 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/base/value_store.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testing/base/test_raw_ostream.h"
#include "toolchain/testing/yaml_test_helpers.h"
namespace Carbon::Testing {
namespace {
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::Not;
using ::testing::Pair;
TEST(ValueStore, Int) {
SharedValueStores value_stores;
IntId id1 = value_stores.ints().Add(llvm::APInt(64, 1));
IntId id2 = value_stores.ints().Add(llvm::APInt(64, 2));
ASSERT_TRUE(id1.is_valid());
ASSERT_TRUE(id2.is_valid());
EXPECT_THAT(id1, Not(Eq(id2)));
EXPECT_THAT(value_stores.ints().Get(id1), Eq(1));
EXPECT_THAT(value_stores.ints().Get(id2), Eq(2));
}
TEST(ValueStore, Real) {
Real real1{.mantissa = llvm::APInt(64, 1),
.exponent = llvm::APInt(64, 11),
.is_decimal = true};
Real real2{.mantissa = llvm::APInt(64, 2),
.exponent = llvm::APInt(64, 22),
.is_decimal = false};
SharedValueStores value_stores;
RealId id1 = value_stores.reals().Add(real1);
RealId id2 = value_stores.reals().Add(real2);
ASSERT_TRUE(id1.is_valid());
ASSERT_TRUE(id2.is_valid());
EXPECT_THAT(id1, Not(Eq(id2)));
const auto& real1_copy = value_stores.reals().Get(id1);
EXPECT_THAT(real1.mantissa, Eq(real1_copy.mantissa));
EXPECT_THAT(real1.exponent, Eq(real1_copy.exponent));
EXPECT_THAT(real1.is_decimal, Eq(real1_copy.is_decimal));
const auto& real2_copy = value_stores.reals().Get(id2);
EXPECT_THAT(real2.mantissa, Eq(real2_copy.mantissa));
EXPECT_THAT(real2.exponent, Eq(real2_copy.exponent));
EXPECT_THAT(real2.is_decimal, Eq(real2_copy.is_decimal));
}
TEST(ValueStore, Float) {
llvm::APFloat float1(1.0);
llvm::APFloat float2(2.0);
SharedValueStores value_stores;
FloatId id1 = value_stores.floats().Add(float1);
FloatId id2 = value_stores.floats().Add(float2);
ASSERT_TRUE(id1.is_valid());
ASSERT_TRUE(id2.is_valid());
EXPECT_THAT(id1, Not(Eq(id2)));
EXPECT_THAT(value_stores.floats().Get(id1).compare(float1),
Eq(llvm::APFloatBase::cmpEqual));
EXPECT_THAT(value_stores.floats().Get(id2).compare(float2),
Eq(llvm::APFloatBase::cmpEqual));
}
TEST(ValueStore, Identifiers) {
std::string a = "a";
std::string b = "b";
SharedValueStores value_stores;
// Make sure reserve works, we use it with identifiers.
value_stores.identifiers().Reserve(100);
auto a_id = value_stores.identifiers().Add(a);
auto b_id = value_stores.identifiers().Add(b);
ASSERT_TRUE(a_id.is_valid());
ASSERT_TRUE(b_id.is_valid());
EXPECT_THAT(a_id, Not(Eq(b_id)));
EXPECT_THAT(value_stores.identifiers().Get(a_id), Eq(a));
EXPECT_THAT(value_stores.identifiers().Get(b_id), Eq(b));
EXPECT_THAT(value_stores.identifiers().Lookup(a), Eq(a_id));
EXPECT_THAT(value_stores.identifiers().Lookup("c"),
Eq(IdentifierId::Invalid));
}
TEST(ValueStore, StringLiterals) {
std::string a = "a";
std::string b = "b";
SharedValueStores value_stores;
auto a_id = value_stores.string_literal_values().Add(a);
auto b_id = value_stores.string_literal_values().Add(b);
ASSERT_TRUE(a_id.is_valid());
ASSERT_TRUE(b_id.is_valid());
EXPECT_THAT(a_id, Not(Eq(b_id)));
EXPECT_THAT(value_stores.string_literal_values().Get(a_id), Eq(a));
EXPECT_THAT(value_stores.string_literal_values().Get(b_id), Eq(b));
EXPECT_THAT(value_stores.string_literal_values().Lookup(a), Eq(a_id));
EXPECT_THAT(value_stores.string_literal_values().Lookup("c"),
Eq(StringLiteralValueId::Invalid));
}
auto MatchSharedValues(testing::Matcher<Yaml::MappingValue> ints,
testing::Matcher<Yaml::MappingValue> reals,
testing::Matcher<Yaml::MappingValue> identifiers,
testing::Matcher<Yaml::MappingValue> strings) -> auto {
return Yaml::IsYaml(Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre(Pair(
"shared_values",
Yaml::Mapping(ElementsAre(Pair("ints", Yaml::Mapping(ints)),
Pair("reals", Yaml::Mapping(reals)),
Pair("identifiers", Yaml::Mapping(identifiers)),
Pair("strings", Yaml::Mapping(strings))))))))));
}
TEST(ValueStore, PrintEmpty) {
SharedValueStores value_stores;
TestRawOstream out;
value_stores.Print(out);
EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()),
MatchSharedValues(IsEmpty(), IsEmpty(), IsEmpty(), IsEmpty()));
}
TEST(ValueStore, PrintVals) {
SharedValueStores value_stores;
llvm::APInt apint(64, 8, /*isSigned=*/true);
value_stores.ints().Add(apint);
value_stores.reals().Add(
Real{.mantissa = apint, .exponent = apint, .is_decimal = true});
value_stores.identifiers().Add("a");
value_stores.string_literal_values().Add("foo'\"baz");
TestRawOstream out;
value_stores.Print(out);
EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()),
MatchSharedValues(
ElementsAre(Pair("int0", Yaml::Scalar("8"))),
ElementsAre(Pair("real0", Yaml::Scalar("8*10^8"))),
ElementsAre(Pair("identifier0", Yaml::Scalar("a"))),
ElementsAre(Pair("string0", Yaml::Scalar("foo'\"baz")))));
}
} // namespace
} // namespace Carbon::Testing