Commit 40b20c94 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[bigint] Faster .toString()

Now that we have advanced division algorithms, we can implement
a divide-and-conquer strategy for toString-conversions, to make
their complexity sub-quadratic.
For example, this speeds up `(2n ** (2n ** 21n)).toString().length`
from 9400 ms to 200 ms on my laptop.

Bug: v8:11515
Change-Id: Id20f7f2928dc7308609f4c1688f32b252e04f433
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3017805Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75880}
parent b2e05eb5
......@@ -21,6 +21,8 @@ constexpr int kBurnikelThreshold = 57;
constexpr int kNewtonInversionThreshold = 50;
// kBarrettThreshold is defined in bigint.h.
constexpr int kToStringFastThreshold = 43;
class ProcessorImpl : public Processor {
public:
explicit ProcessorImpl(Platform* platform);
......@@ -62,6 +64,8 @@ class ProcessorImpl : public Processor {
// {out_length} initially contains the allocated capacity of {out}, and
// upon return will be set to the actual length of the result string.
void ToString(char* out, int* out_length, Digits X, int radix, bool sign);
void ToStringImpl(char* out, int* out_length, Digits X, int radix, bool sign,
bool use_fast_algorithm);
bool should_terminate() { return status_ == Status::kInterrupted; }
......@@ -88,6 +92,20 @@ class ProcessorImpl : public Processor {
Platform* platform_;
};
// These constants are primarily needed for Barrett division in div-barrett.cc,
// and they're also needed by fast to-string conversion in tostring.cc.
constexpr int DivideBarrettScratchSpace(int n) { return n + 2; }
// Local values S and W need "n plus a few" digits; U needs 2*n "plus a few".
// In all tested cases the "few" were either 2 or 3, so give 5 to be safe.
// S and W are not live at the same time.
constexpr int kInvertNewtonExtraSpace = 5;
constexpr int InvertNewtonScratchSpace(int n) {
return 3 * n + 2 * kInvertNewtonExtraSpace;
}
constexpr int InvertScratchSpace(int n) {
return n < kNewtonInversionThreshold ? 2 * n : InvertNewtonScratchSpace(n);
}
#define CHECK(cond) \
if (!(cond)) { \
std::cerr << __FILE__ << ":" << __LINE__ << ": "; \
......
......@@ -23,20 +23,6 @@ namespace bigint {
namespace {
constexpr int DivideBarrettScratchSpace(int n) { return n + 2; }
// Local values S and W need "n plus a few" digits; U needs 2*n "plus a few".
// In all tested cases the "few" were either 2 or 3, so give 5 to be safe.
// S and W are not live at the same time.
constexpr int kInvertNewtonExtraSpace = 5;
constexpr int InvertNewtonScratchSpace(int n) {
return 3 * n + 2 * kInvertNewtonExtraSpace;
}
constexpr int InvertScratchSpace(int n) {
return n < kNewtonInversionThreshold ? 2 * n : InvertNewtonScratchSpace(n);
}
void DcheckIntegerPartRange(Digits X, digit_t min, digit_t max) {
#if DEBUG
digit_t integer_part = X.msd();
......
This diff is collapsed.
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "src/bigint/bigint-internal.h"
......@@ -32,7 +33,8 @@ int PrintHelp(char** argv) {
V(kBurnikel, "burnikel") \
V(kFFT, "fft") \
V(kKaratsuba, "karatsuba") \
V(kToom, "toom")
V(kToom, "toom") \
V(kToString, "tostring")
enum Operation { kNoOp, kList, kTest };
......@@ -158,6 +160,19 @@ class Runner {
error_ = true;
}
void AssertEquals(Digits X, int radix, char* expected, int expected_length,
char* actual, int actual_length) {
if (expected_length == actual_length &&
std::memcmp(expected, actual, actual_length) == 0) {
return;
}
std::cerr << "Input: " << FormatHex(X) << "\n";
std::cerr << "Radix: " << radix << "\n";
std::cerr << "Expected: " << std::string(expected, expected_length) << "\n";
std::cerr << "Actual: " << std::string(actual, actual_length) << "\n";
error_ = true;
}
int RunTest() {
int count = 0;
if (test_ == kBarrett) {
......@@ -180,6 +195,10 @@ class Runner {
for (int i = 0; i < runs_; i++) {
TestToom(&count);
}
} else if (test_ == kToString) {
for (int i = 0; i < runs_; i++) {
TestToString(&count);
}
} else {
DCHECK(false); // Unreachable.
}
......@@ -348,6 +367,30 @@ class Runner {
void TestBarrett(int* count) {}
#endif // V8_ADVANCED_BIGINT_ALGORITHMS
void TestToString(int* count) {
constexpr int kMin = kToStringFastThreshold / 2;
constexpr int kMax = kToStringFastThreshold * 2;
for (int size = kMin; size < kMax; size++) {
ScratchDigits X(size);
GenerateRandom(X);
for (int radix = 2; radix <= 36; radix++) {
int chars_required = ToStringResultLength(X, radix, false);
int result_len = chars_required;
int reference_len = chars_required;
std::unique_ptr<char[]> result(new char[result_len]);
std::unique_ptr<char[]> reference(new char[reference_len]);
processor()->ToStringImpl(result.get(), &result_len, X, radix, false,
true);
processor()->ToStringImpl(reference.get(), &reference_len, X, radix,
false, false);
AssertEquals(X, radix, reference.get(), reference_len, result.get(),
result_len);
if (error_) return;
(*count)++;
}
}
}
int ParseOptions(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--list") == 0) {
......
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