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 {
// the appropriate multiplier, and add the part. O(n²) overall.
void ProcessorImpl::FromStringClassic(RWDigits Z,
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);
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;
Add(Z, (*accumulator->parts_)[i]);
Add(Z, accumulator->heap_parts_[i]);
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) {
if (!accumulator->parts_) {
if (Z.len() > 0) Z[0] = accumulator->part_;
for (int i = 1; i < Z.len(); i++) Z[i] = 0;
if (accumulator->inline_everything_) {
int 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 {
FromStringClassic(Z, accumulator);
}
......
......@@ -960,7 +960,7 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
case State::kZero:
return BigInt::Zero(this->isolate(), allocation_type());
case State::kDone:
return BigInt::Allocate(this->isolate(), accumulator_.get(),
return BigInt::Allocate(this->isolate(), &accumulator_,
this->negative(), allocation_type());
case State::kEmpty:
case State::kRunning:
......@@ -972,26 +972,15 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
private:
template <class Char>
void ParseInternal(Char start) {
accumulator_.reset(
new bigint::FromStringAccumulator(this->radix(), BigInt::kMaxLength));
using Result = bigint::FromStringAccumulator::Result;
Char current = start + this->cursor();
Char end = start + this->length();
current = accumulator_.Parse(current, end, this->radix());
do {
using Result = bigint::FromStringAccumulator::Result;
Result result = accumulator_->ConsumeChar(*current);
if (result != Result::kOk) {
if (result == Result::kMaxSizeExceeded) {
this->set_state(State::kError);
} else {
DCHECK(result == Result::kInvalidChar);
}
break;
}
++current;
} while (current < end);
Result result = accumulator_.result();
if (result == Result::kMaxSizeExceeded) {
return this->set_state(State::kError);
}
if (!this->allow_trailing_junk() && AdvanceToNonspace(&current, end)) {
return this->set_state(State::kJunk);
}
......@@ -1005,7 +994,7 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
: AllocationType::kYoung;
}
std::unique_ptr<bigint::FromStringAccumulator> accumulator_;
bigint::FromStringAccumulator accumulator_{BigInt::kMaxLength};
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