Skip to content

Commit 5825767

Browse files
jnthntatumcopybara-github
authored andcommitted
Memoize enum lookup table in FlatExprBuilder. Switch to using fully qualified name only instead of precomputing all possible ways to reference.
PiperOrigin-RevId: 739351773
1 parent f852cd0 commit 5825767

13 files changed

+554
-209
lines changed

eval/compiler/BUILD

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ cc_test(
7575
"//runtime/internal:issue_collector",
7676
"//runtime/internal:runtime_env",
7777
"//runtime/internal:runtime_env_testing",
78+
"@com_google_absl//absl/base:no_destructor",
7879
"@com_google_absl//absl/base:nullability",
80+
"@com_google_absl//absl/container:flat_hash_map",
7981
"@com_google_absl//absl/status",
8082
"@com_google_absl//absl/status:status_matchers",
8183
"@com_google_absl//absl/status:statusor",
@@ -143,10 +145,12 @@ cc_library(
143145
"@com_google_absl//absl/container:node_hash_map",
144146
"@com_google_absl//absl/functional:any_invocable",
145147
"@com_google_absl//absl/log:absl_check",
148+
"@com_google_absl//absl/log:absl_log",
146149
"@com_google_absl//absl/log:check",
147150
"@com_google_absl//absl/status",
148151
"@com_google_absl//absl/status:statusor",
149152
"@com_google_absl//absl/strings",
153+
"@com_google_absl//absl/synchronization",
150154
"@com_google_absl//absl/types:optional",
151155
"@com_google_absl//absl/types:span",
152156
"@com_google_absl//absl/types:variant",
@@ -368,7 +372,9 @@ cc_test(
368372
"//runtime/internal:issue_collector",
369373
"//runtime/internal:runtime_env",
370374
"//runtime/internal:runtime_env_testing",
375+
"@com_google_absl//absl/base:no_destructor",
371376
"@com_google_absl//absl/base:nullability",
377+
"@com_google_absl//absl/container:flat_hash_map",
372378
"@com_google_absl//absl/status",
373379
"@com_google_absl//absl/status:statusor",
374380
"@com_google_absl//absl/strings",
@@ -417,11 +423,12 @@ cc_library(
417423
"//internal:status_macros",
418424
"//runtime:function_overload_reference",
419425
"//runtime:function_registry",
420-
"//runtime:type_registry",
426+
"@com_google_absl//absl/base:no_destructor",
421427
"@com_google_absl//absl/container:flat_hash_map",
422428
"@com_google_absl//absl/status:statusor",
423429
"@com_google_absl//absl/strings",
424430
"@com_google_absl//absl/types:optional",
431+
"@com_google_absl//absl/types:span",
425432
],
426433
)
427434

@@ -436,19 +443,22 @@ cc_test(
436443
"//base:ast",
437444
"//base:builtins",
438445
"//common:expr",
446+
"//common:value",
439447
"//common/ast:ast_impl",
440448
"//common/ast:expr",
441449
"//common/ast:expr_proto",
442450
"//eval/public:builtin_func_registrar",
443451
"//eval/public:cel_function",
444452
"//eval/public:cel_function_registry",
453+
"//eval/public:cel_value",
445454
"//extensions/protobuf:ast_converters",
446455
"//internal:casts",
447456
"//internal:proto_matchers",
448457
"//internal:testing",
449458
"//runtime:runtime_issue",
450459
"//runtime:type_registry",
451460
"//runtime/internal:issue_collector",
461+
"@com_google_absl//absl/base:no_destructor",
452462
"@com_google_absl//absl/container:flat_hash_map",
453463
"@com_google_absl//absl/log:absl_check",
454464
"@com_google_absl//absl/memory",
@@ -495,8 +505,9 @@ cc_test(
495505
"//eval/public:cel_value",
496506
"//eval/testutil:test_message_cc_proto",
497507
"//internal:testing",
508+
"@com_google_absl//absl/base:no_destructor",
509+
"@com_google_absl//absl/container:flat_hash_map",
498510
"@com_google_absl//absl/status",
499-
"@com_google_absl//absl/types:optional",
500511
"@com_google_absl//absl/types:span",
501512
"@com_google_protobuf//:protobuf",
502513
],
@@ -539,20 +550,27 @@ cc_test(
539550
":flat_expr_builder",
540551
":flat_expr_builder_extensions",
541552
":regex_precompilation_optimization",
553+
":resolver",
554+
"//common:value",
542555
"//common/ast:ast_impl",
543556
"//eval/eval:evaluator_core",
544557
"//eval/public:activation",
545558
"//eval/public:builtin_func_registrar",
546559
"//eval/public:cel_expression",
560+
"//eval/public:cel_function_registry",
547561
"//eval/public:cel_options",
562+
"//eval/public:cel_type_registry",
548563
"//eval/public:cel_value",
549564
"//internal:testing",
550565
"//parser",
551566
"//runtime:runtime_issue",
567+
"//runtime:runtime_options",
552568
"//runtime/internal:issue_collector",
553569
"//runtime/internal:runtime_env",
554570
"//runtime/internal:runtime_env_testing",
571+
"@com_google_absl//absl/base:no_destructor",
555572
"@com_google_absl//absl/base:nullability",
573+
"@com_google_absl//absl/container:flat_hash_map",
556574
"@com_google_absl//absl/status",
557575
"@com_google_cel_spec//proto/cel/expr:checked_cc_proto",
558576
"@com_google_cel_spec//proto/cel/expr:syntax_cc_proto",

eval/compiler/cel_expression_builder_flat_impl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class CelExpressionBuilderFlatImpl : public CelExpressionBuilder {
7474
FlatExprBuilder& flat_expr_builder() { return flat_expr_builder_; }
7575

7676
void set_container(std::string container) override {
77+
flat_expr_builder_.InvalidateResolverIndex();
7778
flat_expr_builder_.set_container(std::move(container));
7879
}
7980

@@ -87,6 +88,7 @@ class CelExpressionBuilderFlatImpl : public CelExpressionBuilder {
8788
// CelValue instances, and to extend the set of types and enums known to
8889
// expressions by registering them ahead of time.
8990
CelTypeRegistry* GetTypeRegistry() const override {
91+
flat_expr_builder_.InvalidateResolverIndex();
9092
return &env_->legacy_type_registry;
9193
}
9294

eval/compiler/constant_folding_test.cc

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
#include "eval/compiler/constant_folding.h"
1616

1717
#include <memory>
18+
#include <string>
1819
#include <utility>
20+
#include <vector>
1921

2022
#include "cel/expr/syntax.pb.h"
23+
#include "absl/base/no_destructor.h"
2124
#include "absl/base/nullability.h"
25+
#include "absl/container/flat_hash_map.h"
2226
#include "absl/status/status.h"
2327
#include "absl/status/statusor.h"
2428
#include "absl/strings/string_view.h"
@@ -68,16 +72,28 @@ using ::google::api::expr::runtime::ProgramOptimizerFactory;
6872
using ::google::api::expr::runtime::Resolver;
6973
using ::testing::SizeIs;
7074

75+
const std::vector<std::string>& EmptyNamespacePrefixes() {
76+
static const absl::NoDestructor<std::vector<std::string>> kEmptyPrefixes(
77+
{""});
78+
return *kEmptyPrefixes;
79+
}
80+
81+
const absl::flat_hash_map<std::string, cel::Value>& EmptyEnumValueMap() {
82+
static const absl::NoDestructor<absl::flat_hash_map<std::string, cel::Value>>
83+
kEmptyEnumValueMap({});
84+
return *kEmptyEnumValueMap;
85+
}
86+
7187
class UpdatedConstantFoldingTest : public testing::Test {
7288
public:
7389
UpdatedConstantFoldingTest()
7490
: env_(NewTestingRuntimeEnv()),
7591
function_registry_(env_->function_registry),
7692
type_registry_(env_->type_registry),
7793
issue_collector_(RuntimeIssue::Severity::kError),
78-
resolver_("", function_registry_, type_registry_,
79-
type_registry_.GetComposedTypeProvider(),
80-
type_registry_.resolveable_enums()) {}
94+
resolver_(EmptyNamespacePrefixes(), EmptyEnumValueMap(),
95+
function_registry_,
96+
type_registry_.GetComposedTypeProvider()) {}
8197

8298
protected:
8399
absl::Nonnull<std::shared_ptr<RuntimeEnv>> env_;

eval/compiler/flat_expr_builder.cc

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "absl/strings/match.h"
4242
#include "absl/strings/numbers.h"
4343
#include "absl/strings/str_cat.h"
44+
#include "absl/strings/str_split.h"
4445
#include "absl/strings/string_view.h"
4546
#include "absl/strings/strip.h"
4647
#include "absl/types/optional.h"
@@ -2459,14 +2460,50 @@ std::vector<ExecutionPathView> FlattenExpressionTable(
24592460

24602461
} // namespace
24612462

2463+
std::shared_ptr<FlatExprBuilder::ResolverIndex>
2464+
FlatExprBuilder::BuildResolverIndex() const {
2465+
auto result = std::make_shared<ResolverIndex>();
2466+
auto& namespace_prefixes = result->namespace_prefixes;
2467+
auto& enum_value_map = result->enum_value_map;
2468+
const auto& resolveable_enums = type_registry_.resolveable_enums();
2469+
2470+
std::string prefix = "";
2471+
namespace_prefixes.push_back(prefix);
2472+
auto container_elements = absl::StrSplit(container_, '.');
2473+
for (const auto& elem : container_elements) {
2474+
// Tolerate trailing / leading '.'.
2475+
if (elem.empty()) {
2476+
continue;
2477+
}
2478+
absl::StrAppend(&prefix, elem, ".");
2479+
// longest prefix first.
2480+
namespace_prefixes.insert(namespace_prefixes.begin(), prefix);
2481+
}
2482+
2483+
for (auto iter = resolveable_enums.begin(); iter != resolveable_enums.end();
2484+
++iter) {
2485+
absl::string_view enum_name = iter->first;
2486+
const auto& enum_type = iter->second;
2487+
2488+
for (const auto& enumerator : enum_type.enumerators) {
2489+
auto key = absl::StrCat(enum_name, ".", enumerator.name);
2490+
enum_value_map[key] = cel::IntValue(enumerator.number);
2491+
}
2492+
}
2493+
2494+
return result;
2495+
}
2496+
24622497
absl::StatusOr<FlatExpression> FlatExprBuilder::CreateExpressionImpl(
24632498
std::unique_ptr<Ast> ast, std::vector<RuntimeIssue>* issues) const {
24642499
RuntimeIssue::Severity max_severity = options_.fail_on_warnings
24652500
? RuntimeIssue::Severity::kWarning
24662501
: RuntimeIssue::Severity::kError;
24672502
IssueCollector issue_collector(max_severity);
2468-
Resolver resolver(container_, function_registry_, type_registry_,
2469-
GetTypeProvider(), type_registry_.resolveable_enums(),
2503+
auto resolver_index = GetResolverIndex();
2504+
Resolver resolver(resolver_index->namespace_prefixes,
2505+
resolver_index->enum_value_map, function_registry_,
2506+
GetTypeProvider(),
24702507
options_.enable_qualified_type_identifiers);
24712508

24722509
std::shared_ptr<google::protobuf::Arena> arena;

eval/compiler/flat_expr_builder.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@
2323
#include <vector>
2424

2525
#include "absl/base/nullability.h"
26+
#include "absl/base/thread_annotations.h"
27+
#include "absl/container/flat_hash_map.h"
28+
#include "absl/log/absl_log.h"
2629
#include "absl/status/statusor.h"
2730
#include "absl/strings/string_view.h"
31+
#include "absl/synchronization/mutex.h"
2832
#include "base/ast.h"
2933
#include "base/type_provider.h"
34+
#include "common/value.h"
3035
#include "eval/compiler/flat_expr_builder_extensions.h"
3136
#include "eval/eval/evaluator_core.h"
3237
#include "runtime/function_registry.h"
@@ -93,11 +98,55 @@ class FlatExprBuilder {
9398
// `optional_type` handling is needed.
9499
void enable_optional_types() { enable_optional_types_ = true; }
95100

101+
// Note: this is a temporary solution to support the legacy expression
102+
// builder which remains mutable after building expressions. This is not
103+
// correct for thread safety (e.g. storing a reference to the type
104+
// registry), however the builder is otherwise thread hostile if used in this
105+
// way so only mitigates some cases.
106+
void InvalidateResolverIndex() const {
107+
absl::MutexLock lock(&resolver_index_mutex_);
108+
if (resolver_index_ != nullptr) {
109+
ABSL_LOG(WARNING)
110+
<< "attempted to update CEL expression builder after use";
111+
}
112+
resolver_index_.reset();
113+
}
114+
96115
private:
97116
const cel::TypeProvider& GetTypeProvider() const;
98117

99118
const absl::Nonnull<std::shared_ptr<const cel::runtime_internal::RuntimeEnv>>
100119
env_;
120+
121+
struct ResolverIndex {
122+
std::vector<std::string> namespace_prefixes;
123+
absl::flat_hash_map<std::string, cel::Value> enum_value_map;
124+
};
125+
126+
std::shared_ptr<ResolverIndex> BuildResolverIndex() const;
127+
128+
std::shared_ptr<ResolverIndex> GetResolverIndex() const {
129+
std::shared_ptr<ResolverIndex> result;
130+
{
131+
absl::ReaderMutexLock lock(&resolver_index_mutex_);
132+
result = resolver_index_;
133+
}
134+
135+
if (result != nullptr) {
136+
return result;
137+
}
138+
// Slow path: build the resolver index.
139+
absl::MutexLock lock(&resolver_index_mutex_);
140+
result = resolver_index_;
141+
if (result != nullptr) {
142+
return result;
143+
}
144+
145+
result = BuildResolverIndex();
146+
resolver_index_ = result;
147+
return result;
148+
}
149+
101150
cel::RuntimeOptions options_;
102151
std::string container_;
103152
bool enable_optional_types_ = false;
@@ -108,6 +157,11 @@ class FlatExprBuilder {
108157
bool use_legacy_type_provider_;
109158
std::vector<std::unique_ptr<AstTransform>> ast_transforms_;
110159
std::vector<ProgramOptimizerFactory> program_optimizers_;
160+
161+
// See note on accessor.
162+
mutable std::shared_ptr<ResolverIndex> resolver_index_
163+
ABSL_GUARDED_BY(resolver_index_mutex_);
164+
mutable absl::Mutex resolver_index_mutex_;
111165
};
112166

113167
} // namespace google::api::expr::runtime

eval/compiler/flat_expr_builder_extensions_test.cc

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
#include "eval/compiler/flat_expr_builder_extensions.h"
1515

1616
#include <memory>
17+
#include <string>
1718
#include <utility>
19+
#include <vector>
1820

21+
#include "absl/base/no_destructor.h"
1922
#include "absl/base/nullability.h"
23+
#include "absl/container/flat_hash_map.h"
2024
#include "absl/status/status.h"
2125
#include "absl/status/status_matchers.h"
2226
#include "absl/status/statusor.h"
@@ -55,15 +59,26 @@ using ::testing::Optional;
5559

5660
using Subexpression = ProgramBuilder::Subexpression;
5761

62+
const std::vector<std::string>& EmptyNamespacePrefixes() {
63+
static const absl::NoDestructor<std::vector<std::string>> kEmptyPrefixes(
64+
{""});
65+
return *kEmptyPrefixes;
66+
}
67+
68+
const absl::flat_hash_map<std::string, cel::Value>& EmptyEnumValueMap() {
69+
static const absl::NoDestructor<absl::flat_hash_map<std::string, cel::Value>>
70+
kEmptyEnumValueMap({});
71+
return *kEmptyEnumValueMap;
72+
}
73+
5874
class PlannerContextTest : public testing::Test {
5975
public:
6076
PlannerContextTest()
6177
: env_(NewTestingRuntimeEnv()),
6278
type_registry_(env_->type_registry),
6379
function_registry_(env_->function_registry),
64-
resolver_("", function_registry_, type_registry_,
65-
type_registry_.GetComposedTypeProvider(),
66-
type_registry_.resolveable_enums()),
80+
resolver_(EmptyNamespacePrefixes(), EmptyEnumValueMap(),
81+
function_registry_, type_registry_.GetComposedTypeProvider()),
6782
issue_collector_(RuntimeIssue::Severity::kError) {}
6883

6984
protected:

0 commit comments

Comments
 (0)