aboutsummaryrefslogtreecommitdiffhomepage
path: root/modules
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2018-06-18 22:25:31 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-06-19 02:50:58 +0000
commitfb3beb0591aed9fd6bf349eb5d08e0e485bcff08 (patch)
treecaf31a17bff46444489c22589d0f8978bb620062 /modules
parent74aa9ab51a76429b20410c94beab859dc39beabd (diff)
[skjson] More short string micro-optimizations
When possible, load 8 chars and mask out the tag and \0 terminator. ~19% faster on a Z840, ~5% faster on a PixelC. Change-Id: I12d4b7d86c92c887b00f5d2480d3ff05a7042df1 Reviewed-on: https://skia-review.googlesource.com/135624 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Mike Klein <mtklein@google.com>
Diffstat (limited to 'modules')
-rw-r--r--modules/skjson/src/SkJSON.cpp86
1 files changed, 67 insertions, 19 deletions
diff --git a/modules/skjson/src/SkJSON.cpp b/modules/skjson/src/SkJSON.cpp
index 23a3d14c9c..2548f4ef41 100644
--- a/modules/skjson/src/SkJSON.cpp
+++ b/modules/skjson/src/SkJSON.cpp
@@ -109,25 +109,73 @@ ArrayValue::ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc) {
//
// The string data plus a null-char terminator are copied over.
//
-StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) {
+namespace {
+
+// An internal string builder with a fast 8 byte short string load path
+// (for the common case where the string is not at the end of the stream).
+class FastString final : public Value {
+public:
+ FastString(const char* src, size_t size, const char* eos, SkArenaAlloc& alloc) {
+ SkASSERT(src <= eos);
+
+ if (size > kMaxInlineStringSize) {
+ this->initLongString(src, size, alloc);
+ SkASSERT(this->getTag() == Tag::kString);
+ return;
+ }
+
+ static_assert(static_cast<uint8_t>(Tag::kShortString) == 0, "please don't break this");
+ static_assert(sizeof(Value) == 8, "");
+
+ // TODO: LIKELY
+ if (src + 7 <= eos) {
+ this->initFastShortString(src, size);
+ } else {
+ this->initShortString(src, size);
+ }
+
+ SkASSERT(this->getTag() == Tag::kShortString);
+ }
+
+private:
static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 1;
- if (size > kMaxInlineStringSize) {
+
+ void initLongString(const char* src, size_t size, SkArenaAlloc& alloc) {
+ SkASSERT(size > kMaxInlineStringSize);
+
this->init_tagged_pointer(Tag::kString, MakeVector<char, 1>(src, size, alloc));
auto* data = this->cast<VectorValue<char, Value::Type::kString>>()->begin();
const_cast<char*>(data)[size] = '\0';
- SkASSERT(this->getTag() == Tag::kString);
- return;
}
- this->init_tagged(Tag::kShortString);
- sk_careful_memcpy(this->cast<char>(), src, size);
+ void initShortString(const char* src, size_t size) {
+ SkASSERT(size <= kMaxInlineStringSize);
+
+ this->init_tagged(Tag::kShortString);
+ sk_careful_memcpy(this->cast<char>(), src, size);
+ // Null terminator provided by init_tagged() above (fData8 is zero-initialized).
+ }
+
+ void initFastShortString(const char* src, size_t size) {
+ SkASSERT(size <= kMaxInlineStringSize);
+
+ // Load 8 chars and mask out the tag and \0 terminator.
+ uint64_t* s64 = this->cast<uint64_t>();
+ memcpy(s64, src, 8);
- // Null terminator provided by init_tagged() above (fData8 is zero-initialized).
- // This is safe because kShortString is also 0 and can act as a terminator when size == 7.
- static_assert(static_cast<uint8_t>(Tag::kShortString) == 0, "please don't break this");
+#if defined(SK_CPU_LENDIAN)
+ *s64 &= 0x00ffffffffffffffULL >> ((kMaxInlineStringSize - size) * 8);
+#else
+ static_assert(false, "Big-endian builds are not supported at this time.");
+#endif
+ }
+};
- SkASSERT(this->getTag() == Tag::kShortString);
+} // namespace
+
+StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) {
+ new (this) FastString(src, size, src, alloc);
}
ObjectValue::ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc) {
@@ -269,8 +317,8 @@ public:
p = skip_ws(p);
if (*p != '"') return this->error(NullValue(), p, "expected object key");
- p = this->matchString(p, p_stop, [this](const char* key, size_t size) {
- this->pushObjectKey(key, size);
+ p = this->matchString(p, p_stop, [this](const char* key, size_t size, const char* eos) {
+ this->pushObjectKey(key, size, eos);
});
if (!p) return NullValue();
@@ -287,8 +335,8 @@ public:
case '\0':
return this->error(NullValue(), p, "unexpected input end");
case '"':
- p = this->matchString(p, p_stop, [this](const char* str, size_t size) {
- this->pushString(str, size);
+ p = this->matchString(p, p_stop, [this](const char* str, size_t size, const char* eos) {
+ this->pushString(str, size, eos);
});
break;
case '[':
@@ -469,11 +517,11 @@ private:
)
}
- void pushObjectKey(const char* key, size_t size) {
+ void pushObjectKey(const char* key, size_t size, const char* eos) {
SkASSERT(fScopeStack.back() >= 0);
SkASSERT(fValueStack.size() >= SkTo<size_t>(fScopeStack.back()));
SkASSERT(!((fValueStack.size() - SkTo<size_t>(fScopeStack.back())) & 1));
- this->pushString(key, size);
+ this->pushString(key, size, eos);
}
void pushTrue() {
@@ -488,8 +536,8 @@ private:
fValueStack.push_back(NullValue());
}
- void pushString(const char* s, size_t size) {
- fValueStack.push_back(StringValue(s, size, fAlloc));
+ void pushString(const char* s, size_t size, const char* eos) {
+ fValueStack.push_back(FastString(s, size, eos, fAlloc));
}
void pushInt32(int32_t i) {
@@ -555,7 +603,7 @@ private:
if (*p == '"') {
// Valid string found.
- func(s_begin, p - s_begin);
+ func(s_begin, p - s_begin, p_stop);
return p + 1;
}