Commit 7817cf1f authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[bigint] Faster parsing from small strings

This patch significantly speeds up parsing of small BigInts. Its
primary idea is to move the loop that's iterating over the string
into the FromStringAccumulator API. That enables using function-
local variables instead of member fields.
A second optimization is to use a stack-allocated digit_t[] array
for small sizes, before falling back to a (comparatively slow)
std::vector.
As a particularly fast path, when this stack-allocated storage is
guaranteed to be enough, we can perform inlined multiply-and-add
steps directly on that data.
Finally, this patch changes the conversion of characters to their
numeric values from computations to a lookup table, which is a bit
faster for radixes <= 10 (where, in the old code, only one range
needed to be checked), and a lot faster for radixes > 10.

Bug: v8:11515
Change-Id: Ifd8ec4799ac34447ba6d4350b7788b559307784c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3064603
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76210}
parent 10d4418f
This diff is collapsed.
...@@ -12,21 +12,51 @@ namespace bigint { ...@@ -12,21 +12,51 @@ namespace bigint {
// the appropriate multiplier, and add the part. O(n²) overall. // the appropriate multiplier, and add the part. O(n²) overall.
void ProcessorImpl::FromStringClassic(RWDigits Z, void ProcessorImpl::FromStringClassic(RWDigits Z,
FromStringAccumulator* accumulator) { FromStringAccumulator* accumulator) {
Z[0] = (*accumulator->parts_)[0]; // We always have at least one part to process.
DCHECK(accumulator->stack_parts_used_ > 0); // NOLINT(readability/check)
Z[0] = accumulator->stack_parts_[0];
RWDigits already_set(Z, 0, 1); RWDigits already_set(Z, 0, 1);
for (int i = 1; i < Z.len(); i++) Z[i] = 0; for (int i = 1; i < Z.len(); i++) Z[i] = 0;
for (int i = 1; i < accumulator->parts_size(); i++) {
MultiplySingle(Z, already_set, (*accumulator->multipliers_)[i]); // The {FromStringAccumulator} uses stack-allocated storage for the first
// few parts; if heap storage is used at all then all parts are copied there.
int num_stack_parts = accumulator->stack_parts_used_;
if (num_stack_parts == 1) return;
const std::vector<digit_t>& heap_parts = accumulator->heap_parts_;
int num_heap_parts = static_cast<int>(heap_parts.size());
// All multipliers are the same, except possibly for the last.
const digit_t max_multiplier = accumulator->max_multiplier_;
if (num_heap_parts == 0) {
for (int i = 1; i < num_stack_parts - 1; i++) {
MultiplySingle(Z, already_set, max_multiplier);
Add(Z, accumulator->stack_parts_[i]);
already_set.set_len(already_set.len() + 1);
}
MultiplySingle(Z, already_set, accumulator->last_multiplier_);
Add(Z, accumulator->stack_parts_[num_stack_parts - 1]);
return;
}
// Parts are stored on the heap.
for (int i = 1; i < num_heap_parts - 1; i++) {
MultiplySingle(Z, already_set, max_multiplier);
if (should_terminate()) return; if (should_terminate()) return;
Add(Z, (*accumulator->parts_)[i]); Add(Z, accumulator->heap_parts_[i]);
already_set.set_len(already_set.len() + 1); already_set.set_len(already_set.len() + 1);
} }
MultiplySingle(Z, already_set, accumulator->last_multiplier_);
Add(Z, accumulator->heap_parts_.back());
} }
void ProcessorImpl::FromString(RWDigits Z, FromStringAccumulator* accumulator) { void ProcessorImpl::FromString(RWDigits Z, FromStringAccumulator* accumulator) {
if (!accumulator->parts_) { if (accumulator->inline_everything_) {
if (Z.len() > 0) Z[0] = accumulator->part_; int i = 0;
for (int i = 1; i < Z.len(); i++) Z[i] = 0; for (; i < accumulator->stack_parts_used_; i++) {
Z[i] = accumulator->stack_parts_[i];
}
for (; i < Z.len(); i++) Z[i] = 0;
} else if (accumulator->stack_parts_used_ == 0) {
for (int i = 0; i < Z.len(); i++) Z[i] = 0;
} else { } else {
FromStringClassic(Z, accumulator); FromStringClassic(Z, accumulator);
} }
......
...@@ -960,7 +960,7 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> { ...@@ -960,7 +960,7 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
case State::kZero: case State::kZero:
return BigInt::Zero(this->isolate(), allocation_type()); return BigInt::Zero(this->isolate(), allocation_type());
case State::kDone: case State::kDone:
return BigInt::Allocate(this->isolate(), accumulator_.get(), return BigInt::Allocate(this->isolate(), &accumulator_,
this->negative(), allocation_type()); this->negative(), allocation_type());
case State::kEmpty: case State::kEmpty:
case State::kRunning: case State::kRunning:
...@@ -972,26 +972,15 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> { ...@@ -972,26 +972,15 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
private: private:
template <class Char> template <class Char>
void ParseInternal(Char start) { void ParseInternal(Char start) {
accumulator_.reset( using Result = bigint::FromStringAccumulator::Result;
new bigint::FromStringAccumulator(this->radix(), BigInt::kMaxLength));
Char current = start + this->cursor(); Char current = start + this->cursor();
Char end = start + this->length(); Char end = start + this->length();
current = accumulator_.Parse(current, end, this->radix());
do { Result result = accumulator_.result();
using Result = bigint::FromStringAccumulator::Result; if (result == Result::kMaxSizeExceeded) {
Result result = accumulator_->ConsumeChar(*current); return this->set_state(State::kError);
if (result != Result::kOk) { }
if (result == Result::kMaxSizeExceeded) {
this->set_state(State::kError);
} else {
DCHECK(result == Result::kInvalidChar);
}
break;
}
++current;
} while (current < end);
if (!this->allow_trailing_junk() && AdvanceToNonspace(&current, end)) { if (!this->allow_trailing_junk() && AdvanceToNonspace(&current, end)) {
return this->set_state(State::kJunk); return this->set_state(State::kJunk);
} }
...@@ -1005,7 +994,7 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> { ...@@ -1005,7 +994,7 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
: AllocationType::kYoung; : AllocationType::kYoung;
} }
std::unique_ptr<bigint::FromStringAccumulator> accumulator_; bigint::FromStringAccumulator accumulator_{BigInt::kMaxLength};
Behavior behavior_; Behavior behavior_;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment