Optimise the layout of the `RE2` class.
In particular, `error_arg_` should be a pointer... that will almost
always be `empty_string`. Also, separate the relatively cold fields
from the relatively hot fields to be slightly friendlier to caches.
Bump SONAME accordingly.
Credit and gratitude to Clement Courbet for his suggestions.
Change-Id: I6553878bbae5e2aae650ad37a7755a833708af4b
Reviewed-on: https://code-review.googlesource.com/c/re2/+/60190
Reviewed-by: Perry Lorier <perryl@google.com>
Reviewed-by: Paul Wankadia <junyer@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e6828d8..f94b423 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,7 +19,7 @@
# ABI version
# http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
-set(SONAME 9)
+set(SONAME 10)
set(EXTRA_TARGET_LINK_LIBRARIES)
diff --git a/Makefile b/Makefile
index 197c2b3..c24fd57 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@
# ABI version
# http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
-SONAME=9
+SONAME=10
# To rebuild the Tables generated by Perl and Python scripts (requires Internet
# access for Unicode data), uncomment the following line:
diff --git a/re2/re2.cc b/re2/re2.cc
index c027133..e127e21 100644
--- a/re2/re2.cc
+++ b/re2/re2.cc
@@ -43,11 +43,11 @@
const int RE2::Options::kDefaultMaxMem; // initialized in re2.h
RE2::Options::Options(RE2::CannedOptions opt)
- : encoding_(opt == RE2::Latin1 ? EncodingLatin1 : EncodingUTF8),
+ : max_mem_(kDefaultMaxMem),
+ encoding_(opt == RE2::Latin1 ? EncodingLatin1 : EncodingUTF8),
posix_syntax_(opt == RE2::POSIX),
longest_match_(opt == RE2::POSIX),
log_errors_(opt != RE2::Quiet),
- max_mem_(kDefaultMaxMem),
literal_(false),
never_nl_(false),
dot_nl_(false),
@@ -178,18 +178,20 @@
empty_group_names = new std::map<int, std::string>;
});
- pattern_.assign(pattern.data(), pattern.size());
+ pattern_ = new std::string(pattern);
options_.Copy(options);
entire_regexp_ = NULL;
- error_ = empty_string;
- error_code_ = NoError;
- error_arg_.clear();
- prefix_.clear();
- prefix_foldcase_ = false;
suffix_regexp_ = NULL;
- prog_ = NULL;
+ error_ = empty_string;
+ error_arg_ = empty_string;
+
num_captures_ = -1;
+ error_code_ = NoError;
+ longest_match_ = options_.longest_match();
is_one_pass_ = false;
+ prefix_foldcase_ = false;
+ prefix_.clear();
+ prog_ = NULL;
rprog_ = NULL;
named_groups_ = NULL;
@@ -197,25 +199,29 @@
RegexpStatus status;
entire_regexp_ = Regexp::Parse(
- pattern_,
+ *pattern_,
static_cast<Regexp::ParseFlags>(options_.ParseFlags()),
&status);
if (entire_regexp_ == NULL) {
if (options_.log_errors()) {
- LOG(ERROR) << "Error parsing '" << trunc(pattern_) << "': "
+ LOG(ERROR) << "Error parsing '" << trunc(*pattern_) << "': "
<< status.Text();
}
error_ = new std::string(status.Text());
error_code_ = RegexpErrorToRE2(status.code());
- error_arg_ = std::string(status.error_arg());
+ error_arg_ = new std::string(status.error_arg());
return;
}
+ bool foldcase;
re2::Regexp* suffix;
- if (entire_regexp_->RequiredPrefix(&prefix_, &prefix_foldcase_, &suffix))
+ if (entire_regexp_->RequiredPrefix(&prefix_, &foldcase, &suffix)) {
+ prefix_foldcase_ = foldcase;
suffix_regexp_ = suffix;
- else
+ }
+ else {
suffix_regexp_ = entire_regexp_->Incref();
+ }
// Two thirds of the memory goes to the forward Prog,
// one third to the reverse prog, because the forward
@@ -223,7 +229,7 @@
prog_ = suffix_regexp_->CompileToProg(options_.max_mem()*2/3);
if (prog_ == NULL) {
if (options_.log_errors())
- LOG(ERROR) << "Error compiling '" << trunc(pattern_) << "'";
+ LOG(ERROR) << "Error compiling '" << trunc(*pattern_) << "'";
error_ = new std::string("pattern too large - compile failed");
error_code_ = RE2::ErrorPatternTooLarge;
return;
@@ -249,7 +255,8 @@
re->suffix_regexp_->CompileToReverseProg(re->options_.max_mem() / 3);
if (re->rprog_ == NULL) {
if (re->options_.log_errors())
- LOG(ERROR) << "Error reverse compiling '" << trunc(re->pattern_) << "'";
+ LOG(ERROR) << "Error reverse compiling '" << trunc(*re->pattern_)
+ << "'";
// We no longer touch error_ and error_code_ because failing to compile
// the reverse Prog is not a showstopper: falling back to NFA execution
// is fine. More importantly, an RE2 object is supposed to be logically
@@ -261,18 +268,21 @@
}
RE2::~RE2() {
+ if (group_names_ != empty_group_names)
+ delete group_names_;
+ if (named_groups_ != empty_named_groups)
+ delete named_groups_;
+ delete rprog_;
+ delete prog_;
+ if (error_arg_ != empty_string)
+ delete error_arg_;
+ if (error_ != empty_string)
+ delete error_;
if (suffix_regexp_)
suffix_regexp_->Decref();
if (entire_regexp_)
entire_regexp_->Decref();
- delete prog_;
- delete rprog_;
- if (error_ != empty_string)
- delete error_;
- if (named_groups_ != NULL && named_groups_ != empty_named_groups)
- delete named_groups_;
- if (group_names_ != NULL && group_names_ != empty_group_names)
- delete group_names_;
+ delete pattern_;
}
int RE2::ProgramSize() const {
@@ -686,9 +696,8 @@
}
Prog::Anchor anchor = Prog::kUnanchored;
- Prog::MatchKind kind = Prog::kFirstMatch;
- if (options_.longest_match())
- kind = Prog::kLongestMatch;
+ Prog::MatchKind kind =
+ longest_match_ ? Prog::kLongestMatch : Prog::kFirstMatch;
bool can_one_pass = is_one_pass_ && ncap <= Prog::kMaxOnePassCapture;
bool can_bit_state = prog_->CanBitState();
@@ -720,7 +729,7 @@
if (dfa_failed) {
if (options_.log_errors())
LOG(ERROR) << "DFA out of memory: "
- << "pattern length " << pattern_.size() << ", "
+ << "pattern length " << pattern_->size() << ", "
<< "program size " << prog->size() << ", "
<< "list count " << prog->list_count() << ", "
<< "bytemap range " << prog->bytemap_range();
@@ -740,7 +749,7 @@
if (dfa_failed) {
if (options_.log_errors())
LOG(ERROR) << "DFA out of memory: "
- << "pattern length " << pattern_.size() << ", "
+ << "pattern length " << pattern_->size() << ", "
<< "program size " << prog_->size() << ", "
<< "list count " << prog_->list_count() << ", "
<< "bytemap range " << prog_->bytemap_range();
@@ -766,7 +775,7 @@
if (dfa_failed) {
if (options_.log_errors())
LOG(ERROR) << "DFA out of memory: "
- << "pattern length " << pattern_.size() << ", "
+ << "pattern length " << pattern_->size() << ", "
<< "program size " << prog->size() << ", "
<< "list count " << prog->list_count() << ", "
<< "bytemap range " << prog->bytemap_range();
@@ -809,7 +818,7 @@
if (dfa_failed) {
if (options_.log_errors())
LOG(ERROR) << "DFA out of memory: "
- << "pattern length " << pattern_.size() << ", "
+ << "pattern length " << pattern_->size() << ", "
<< "program size " << prog_->size() << ", "
<< "list count " << prog_->list_count() << ", "
<< "bytemap range " << prog_->bytemap_range();
diff --git a/re2/re2.h b/re2/re2.h
index df32ce3..38fdcb7 100644
--- a/re2/re2.h
+++ b/re2/re2.h
@@ -288,7 +288,7 @@
// The string specification for this RE2. E.g.
// RE2 re("ab*c?d+");
// re.pattern(); // "ab*c?d+"
- const std::string& pattern() const { return pattern_; }
+ const std::string& pattern() const { return *pattern_; }
// If RE2 could not be created properly, returns an error string.
// Else returns the empty string.
@@ -300,7 +300,7 @@
// If RE2 could not be created properly, returns the offending
// portion of the regexp.
- const std::string& error_arg() const { return error_arg_; }
+ const std::string& error_arg() const { return *error_arg_; }
// Returns the program size, a very approximate measure of a regexp's "cost".
// Larger numbers are more expensive than smaller numbers.
@@ -653,11 +653,11 @@
};
Options() :
+ max_mem_(kDefaultMaxMem),
encoding_(EncodingUTF8),
posix_syntax_(false),
longest_match_(false),
log_errors_(true),
- max_mem_(kDefaultMaxMem),
literal_(false),
never_nl_(false),
dot_nl_(false),
@@ -670,6 +670,9 @@
/*implicit*/ Options(CannedOptions);
+ int64_t max_mem() const { return max_mem_; }
+ void set_max_mem(int64_t m) { max_mem_ = m; }
+
Encoding encoding() const { return encoding_; }
void set_encoding(Encoding encoding) { encoding_ = encoding; }
@@ -682,9 +685,6 @@
bool log_errors() const { return log_errors_; }
void set_log_errors(bool b) { log_errors_ = b; }
- int64_t max_mem() const { return max_mem_; }
- void set_max_mem(int64_t m) { max_mem_ = m; }
-
bool literal() const { return literal_; }
void set_literal(bool b) { literal_ = b; }
@@ -716,11 +716,11 @@
int ParseFlags() const;
private:
+ int64_t max_mem_;
Encoding encoding_;
bool posix_syntax_;
bool longest_match_;
bool log_errors_;
- int64_t max_mem_;
bool literal_;
bool never_nl_;
bool dot_nl_;
@@ -753,18 +753,23 @@
re2::Prog* ReverseProg() const;
- std::string pattern_; // string regular expression
- Options options_; // option flags
- re2::Regexp* entire_regexp_; // parsed regular expression
- const std::string* error_; // error indicator (or points to empty string)
- ErrorCode error_code_; // error code
- std::string error_arg_; // fragment of regexp showing error
- std::string prefix_; // required prefix (before suffix_regexp_)
- bool prefix_foldcase_; // prefix_ is ASCII case-insensitive
- re2::Regexp* suffix_regexp_; // parsed regular expression, prefix_ removed
- re2::Prog* prog_; // compiled program for regexp
- int num_captures_; // number of capturing groups
- bool is_one_pass_; // can use prog_->SearchOnePass?
+ // First cache line is relatively cold fields.
+ const std::string* pattern_; // string regular expression
+ Options options_; // option flags
+ re2::Regexp* entire_regexp_; // parsed regular expression
+ re2::Regexp* suffix_regexp_; // parsed regular expression, prefix_ removed
+ const std::string* error_; // error indicator (or points to empty string)
+ const std::string* error_arg_; // fragment of regexp showing error (or ditto)
+
+ // Second cache line is relatively hot fields.
+ // These are ordered oddly to pack everything.
+ int num_captures_; // number of capturing groups
+ ErrorCode error_code_ : 29; // error code (29 bits is more than enough)
+ bool longest_match_ : 1; // cached copy of options_.longest_match()
+ bool is_one_pass_ : 1; // can use prog_->SearchOnePass?
+ bool prefix_foldcase_ : 1; // prefix_ is ASCII case-insensitive
+ std::string prefix_; // required prefix (before suffix_regexp_)
+ re2::Prog* prog_; // compiled program for regexp
// Reverse Prog for DFA execution only
mutable re2::Prog* rprog_;