Bug 1493150 - Optimize structured cloning by reducing string allocations. r=jandem
authorTom Schuster <evilpies@gmail.com>
Mon, 24 Sep 2018 19:17:10 +0200
changeset 495621 0bb3d036d145a3a7229967c7d39944d3731b4d2b
parent 495620 7a57488417574dbc5cfe2995e02a011cc887d6be
child 495622 9f1fd88190e39614a71b069403b92169f38afc8e
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1493150
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1493150 - Optimize structured cloning by reducing string allocations. r=jandem
js/src/vm/InlineCharBuffer-inl.h
js/src/vm/StringType-inl.h
js/src/vm/StringType.cpp
js/src/vm/StructuredClone.cpp
--- a/js/src/vm/InlineCharBuffer-inl.h
+++ b/js/src/vm/InlineCharBuffer-inl.h
@@ -113,17 +113,22 @@ class MOZ_NON_PARAM InlineCharBuffer
     JSString* toStringDontDeflate(JSContext* cx, size_t length)
     {
         MOZ_ASSERT(length == lastRequestedLength);
 
         if (JSInlineString::lengthFits<CharT>(length)) {
             MOZ_ASSERT(!heapStorage,
                        "expected only inline storage when length fits in inline string");
 
-            return NewStringCopyNDontDeflate<CanGC>(cx, inlineStorage, length);
+            if (JSString* str = TryEmptyOrStaticString(cx, inlineStorage, length)) {
+                return str;
+            }
+
+            mozilla::Range<const CharT> range(inlineStorage, length);
+            return NewInlineString<CanGC>(cx, range);
         }
 
         MOZ_ASSERT(heapStorage, "heap storage was not allocated for non-inline string");
 
         heapStorage.get()[length] = '\0'; // Null-terminate
         return NewStringDontDeflate<CanGC>(cx, std::move(heapStorage), length);
     }
 
--- a/js/src/vm/StringType-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -83,16 +83,36 @@ NewInlineString(JSContext* cx, HandleLin
     }
 
     JS::AutoCheckCannotGC nogc;
     mozilla::PodCopy(chars, base->chars<CharT>(nogc) + start, length);
     chars[length] = 0;
     return s;
 }
 
+template <typename CharT>
+static MOZ_ALWAYS_INLINE JSFlatString*
+TryEmptyOrStaticString(JSContext* cx, const CharT* chars, size_t n)
+{
+    // Measurements on popular websites indicate empty strings are pretty common
+    // and most strings with length 1 or 2 are in the StaticStrings table. For
+    // length 3 strings that's only about 1%, so we check n <= 2.
+    if (n <= 2) {
+        if (n == 0) {
+            return cx->emptyString();
+        }
+
+        if (JSFlatString* str = cx->staticStrings().lookup(chars, n)) {
+            return str;
+        }
+    }
+
+    return nullptr;
+}
+
 } /* namespace js */
 
 MOZ_ALWAYS_INLINE bool
 JSString::validateLength(JSContext* maybecx, size_t length)
 {
     if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) {
         js::ReportAllocationOverflow(maybecx);
         return false;
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -1580,36 +1580,16 @@ NewInlineStringDeflated(JSContext* cx, m
     for (size_t i = 0; i < len; i++) {
         MOZ_ASSERT(chars[i] <= JSString::MAX_LATIN1_CHAR);
         storage[i] = Latin1Char(chars[i]);
     }
     storage[len] = '\0';
     return str;
 }
 
-template <typename CharT>
-static MOZ_ALWAYS_INLINE JSFlatString*
-TryEmptyOrStaticString(JSContext* cx, const CharT* chars, size_t n)
-{
-    // Measurements on popular websites indicate empty strings are pretty common
-    // and most strings with length 1 or 2 are in the StaticStrings table. For
-    // length 3 strings that's only about 1%, so we check n <= 2.
-    if (n <= 2) {
-        if (n == 0) {
-            return cx->emptyString();
-        }
-
-        if (JSFlatString* str = cx->staticStrings().lookup(chars, n)) {
-            return str;
-        }
-    }
-
-    return nullptr;
-}
-
 template <AllowGC allowGC>
 static JSFlatString*
 NewStringDeflated(JSContext* cx, const char16_t* s, size_t n)
 {
     if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n)) {
         return str;
     }
 
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -51,16 +51,17 @@
 #include "vm/JSContext.h"
 #include "vm/RegExpObject.h"
 #include "vm/SavedFrame.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 #include "wasm/WasmJS.h"
 
+#include "vm/InlineCharBuffer-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::BitwiseCast;
 using mozilla::NativeEndian;
 using mozilla::NumbersAreIdentical;
@@ -2160,21 +2161,22 @@ template <typename CharT>
 JSString*
 JSStructuredCloneReader::readStringImpl(uint32_t nchars)
 {
     if (nchars > JSString::MAX_LENGTH) {
         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
                                   "string length");
         return nullptr;
     }
-    UniquePtr<CharT[], JS::FreePolicy> chars = AllocateChars<CharT>(context(), nchars);
-    if (!chars || !in.readChars(chars.get(), nchars)) {
+
+    InlineCharBuffer<CharT> chars;
+    if (!chars.maybeAlloc(context(), nchars) || !in.readChars(chars.get(), nchars)) {
         return nullptr;
     }
-    return NewString<CanGC>(context(), std::move(chars), nchars);
+    return chars.toStringDontDeflate(context(), nchars);
 }
 
 JSString*
 JSStructuredCloneReader::readString(uint32_t data)
 {
     uint32_t nchars = data & JS_BITMASK(31);
     bool latin1 = data & (1 << 31);
     return latin1 ? readStringImpl<Latin1Char>(nchars) : readStringImpl<char16_t>(nchars);