mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 08:54:47 +01:00
Use inline small storage for small SemIR ID sets in toolchain (#7796)
Apply SmallSize = 16 to frequent identifier and instruction/function sets in ScopeStack, Class, FacetType, and SpecificCoalescer to avoid dynamic heap allocations on small scopes. Also defer dest_field_names set allocation in struct conversion and provide default KeyContext for SetBase. This was found by inspection, but does seem to be a clear 1.5% win on overall compile time. ``` Ran baseline and experiment 10 times on 128 x 2450 MHz CPUs CPU caches: L1 Data 32Ki L1 Instruction 32Ki L2 Unified 512Ki L3 Unified 32Mi Load avg: 1.2041 1.16895 2.86133 Computing statistically significant deltas only wherethe P-value < 𝛂 of 0.05 Metric key: BenchmarkName... 👍 <delta> p=<U-test P-value> baseline: <median> ± <% at 95th conf> experiment: <median> ± <% at 95th conf> Benchmark ┃ CPU Time ┃ CYCLES ┃ INSTRUCTIONS ┃ Lines ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━ BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/256... │ 👍 -1.768% p=0.0346 │ 👍 -1.683% p=0.0346 │ 👍 0.160% p=0.000428 │ ~ baseline: │ 49.11 ms ± 1.363% │ 155.6 M ± 1.875% │ 293.9 M ± 0.015% │ 4.093 k ± 1.360% experiment: │ 48.24 ms ± 2.213% │ 153 M ± 2.465% │ 293.4 M ± 0.081% │ 4.166 k ± 2.165% │ │ │ │ BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/1024.. │ ?? p=0.159 │ ?? p=0.067 │ 👍 0.153% p=0.000249 │ ~ baseline: │ 50.61 ms ± 2.379% │ 160.7 M ± 2.777% │ 309.7 M ± 0.015% │ 19.96 k ± 2.326% experiment: │ 50.32 ms ± 0.971% │ 159.6 M ± 0.787% │ 309.2 M ± 0.086% │ 20.07 k ± 0.980% │ │ │ │ BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/4096.. │ 👍 -1.593% p=0.0112 │ 👍 -1.632% p=0.00743 │ 👍 0.138% p=0.000328 │ ~ baseline: │ 58.58 ms ± 1.673% │ 186.5 M ± 1.487% │ 371.2 M ± 0.102% │ 70.96 k ± 1.645% experiment: │ 57.65 ms ± 1.032% │ 183.5 M ± 1.079% │ 370.7 M ± 0.091% │ 72.11 k ± 1.043% │ │ │ │ BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/16384. │ 👍 -1.695% p=0.029 │ 👍 -1.823% p=0.0411 │ 👍 0.221% p=0.000428 │ ~ baseline: │ 90.07 ms ± 3.686% │ 287.1 M ± 3.694% │ 618.9 M ± 0.106% │ 186.9 k ± 3.555% experiment: │ 88.55 ms ± 1.891% │ 281.8 M ± 2.142% │ 617.6 M ± 0.158% │ 190.1 k ± 1.856% │ │ │ │ BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/65536. │ 👍 -1.797% p=0.0201 │ 👍 -1.762% p=0.0201 │ 👍 0.222% p=0.000328 │ ~ baseline: │ 222.1 ms ± 2.560% │ 711 M ± 2.417% │ 1.613 G ± 0.158% │ 304.2 k ± 2.496% experiment: │ 218.1 ms ± 1.950% │ 698.5 M ± 2.010% │ 1.61 G ± 0.075% │ 309.7 k ± 1.989% │ │ │ │ BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/262144 │ ?? p=0.398 │ ?? p=0.36 │ 👍 0.166% p=0.000931 │ ~ baseline: │ 782.2 ms ± 2.666% │ 2.502 G ± 2.546% │ 5.596 G ± 0.038% │ 345.8 k ± 2.597% experiment: │ 780.6 ms ± 4.249% │ 2.483 G ± 4.691% │ 5.587 G ± 0.024% │ 346.5 k ± 4.075% │ │ │ │ ``` Assisted-by: Antigravity with Gemini
This commit is contained in:
+1
-1
@@ -131,7 +131,7 @@ class SetView : RawHashtable::ViewImpl<InputKeyT, void, InputKeyContextT> {
|
||||
// A pointer or reference to this type is the preferred way to pass a mutable
|
||||
// handle to a `Set` type across API boundaries as it avoids encoding specific
|
||||
// SSO sizing information while providing a near-complete mutable API.
|
||||
template <typename InputKeyT, typename InputKeyContextT>
|
||||
template <typename InputKeyT, typename InputKeyContextT = DefaultKeyContext>
|
||||
class SetBase
|
||||
: protected RawHashtable::BaseImpl<InputKeyT, void, InputKeyContextT> {
|
||||
protected:
|
||||
|
||||
@@ -222,7 +222,7 @@ static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id,
|
||||
};
|
||||
|
||||
llvm::SmallVector<SemIR::InstId> vtable;
|
||||
Set<SemIR::FunctionId> implemented_impls;
|
||||
Set<SemIR::FunctionId, 16> implemented_impls;
|
||||
bool carbon_native_vtable = true;
|
||||
|
||||
// Add vtable entries from the base class, updating them to point to a derived
|
||||
|
||||
@@ -649,15 +649,15 @@ static auto ConvertStructToStructOrClass(
|
||||
value_id = MaterializeIfInitializer(context, value_id);
|
||||
}
|
||||
|
||||
Set<SemIR::NameId> dest_field_names;
|
||||
for (auto field : dest_elem_fields) {
|
||||
dest_field_names.Insert(field.name_id);
|
||||
}
|
||||
|
||||
// Prepare to look up fields in the source by index. Also check for
|
||||
// source fields that don't match any field in the destination.
|
||||
Map<SemIR::NameId, int32_t> src_field_indexes;
|
||||
if (src_type.fields_id != dest_type.fields_id) {
|
||||
Set<SemIR::NameId, 16> dest_field_names;
|
||||
for (auto field : dest_elem_fields) {
|
||||
dest_field_names.Insert(field.name_id);
|
||||
}
|
||||
|
||||
for (auto [i, field] : llvm::enumerate(src_elem_fields)) {
|
||||
if (!dest_field_names.Lookup(field.name_id)) {
|
||||
if (target.diagnose) {
|
||||
|
||||
@@ -550,7 +550,7 @@ auto FindWhere(Context& context, SemIR::ConstantId const_id) -> bool {
|
||||
|
||||
private:
|
||||
bool* found_;
|
||||
Set<SemIR::InstId> searched_;
|
||||
Set<SemIR::InstId, 16> searched_;
|
||||
};
|
||||
|
||||
if (!const_id.is_constant()) {
|
||||
|
||||
@@ -359,7 +359,7 @@ class ScopeStack {
|
||||
|
||||
// Names which are registered with lexical_lookup_, and will need to be
|
||||
// unregistered when the scope ends.
|
||||
Set<SemIR::NameId> names = {};
|
||||
Set<SemIR::NameId, 16> names = {};
|
||||
};
|
||||
|
||||
// A scope in which `return` can be used.
|
||||
|
||||
@@ -69,6 +69,7 @@ cc_library(
|
||||
"//common:map",
|
||||
"//common:pretty_stack_trace_function",
|
||||
"//common:raw_string_ostream",
|
||||
"//common:set",
|
||||
"//common:vlog",
|
||||
"//toolchain/base:fixed_size_value_store",
|
||||
"//toolchain/base:kind_switch",
|
||||
|
||||
@@ -59,7 +59,7 @@ auto SpecificCoalescer::CoalesceEquivalentSpecifics(
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>, 16>
|
||||
visited_equivalent_specifics;
|
||||
InsertPair(specifics[i], specifics[j], visited_equivalent_specifics);
|
||||
// Function type information matches; check usages inside the function
|
||||
@@ -205,7 +205,7 @@ auto SpecificCoalescer::AreFunctionTypesEquivalent(
|
||||
|
||||
auto SpecificCoalescer::AreFunctionBodiesEquivalent(
|
||||
SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>&
|
||||
SetBase<std::pair<SemIR::SpecificId, SemIR::SpecificId>>&
|
||||
visited_equivalent_specifics) -> bool {
|
||||
llvm::SmallVector<std::pair<SemIR::SpecificId, SemIR::SpecificId>> worklist;
|
||||
worklist.push_back({specific_id1, specific_id2});
|
||||
@@ -251,7 +251,7 @@ auto SpecificCoalescer::AreFunctionBodiesEquivalent(
|
||||
|
||||
auto SpecificCoalescer::InsertPair(
|
||||
SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
|
||||
SetBase<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
|
||||
-> bool {
|
||||
if (specific_id1.index > specific_id2.index) {
|
||||
std::swap(specific_id1.index, specific_id2.index);
|
||||
@@ -263,7 +263,7 @@ auto SpecificCoalescer::InsertPair(
|
||||
|
||||
auto SpecificCoalescer::ContainsPair(
|
||||
SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
|
||||
const Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
|
||||
SetView<std::pair<SemIR::SpecificId, SemIR::SpecificId>> set_of_pairs)
|
||||
-> bool {
|
||||
if (specific_id1.index > specific_id2.index) {
|
||||
std::swap(specific_id1.index, specific_id2.index);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef CARBON_TOOLCHAIN_LOWER_SPECIFIC_COALESCER_H_
|
||||
#define CARBON_TOOLCHAIN_LOWER_SPECIFIC_COALESCER_H_
|
||||
|
||||
#include "common/set.h"
|
||||
#include "llvm/Support/BLAKE3.h"
|
||||
#include "toolchain/lower/context.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
@@ -87,7 +88,7 @@ class SpecificCoalescer {
|
||||
// found, if the two specifics given as arguments are found to be equivalent.
|
||||
auto AreFunctionBodiesEquivalent(
|
||||
SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>&
|
||||
SetBase<std::pair<SemIR::SpecificId, SemIR::SpecificId>>&
|
||||
visited_equivalent_specifics) -> bool;
|
||||
|
||||
// Given an equivalent pair of specifics, updates the canonical specific to
|
||||
@@ -118,13 +119,13 @@ class SpecificCoalescer {
|
||||
// checks entry already existed if it cannot be inserted.
|
||||
auto InsertPair(
|
||||
SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
|
||||
SetBase<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
|
||||
-> bool;
|
||||
|
||||
// Checks if a pair is contained into a set of pairs, in canonical form.
|
||||
auto ContainsPair(
|
||||
SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
|
||||
const Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
|
||||
SetView<std::pair<SemIR::SpecificId, SemIR::SpecificId>> set_of_pairs)
|
||||
-> bool;
|
||||
|
||||
// The optional vlog stream.
|
||||
@@ -154,7 +155,7 @@ class SpecificCoalescer {
|
||||
|
||||
// Non-equivalent specifics found.
|
||||
// TODO: Revisit this due to its quadratic space growth.
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>
|
||||
Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>, 16>
|
||||
non_equivalent_specifics_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user