diff options
author | Florin Malita <fmalita@chromium.org> | 2018-06-18 22:25:31 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-06-19 02:50:58 +0000 |
commit | fb3beb0591aed9fd6bf349eb5d08e0e485bcff08 (patch) | |
tree | caf31a17bff46444489c22589d0f8978bb620062 /modules | |
parent | 74aa9ab51a76429b20410c94beab859dc39beabd (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.cpp | 86 |
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; } |