Changeset 245194 in webkit


Ignore:
Timestamp:
May 10, 2019 1:49:35 PM (5 years ago)
Author:
ysuzuki@apple.com
Message:

[JSC] String substring operation should return ropes consistently
https://bugs.webkit.org/show_bug.cgi?id=197765
<rdar://problem/37689944>

Reviewed by Michael Saboff.

Currently we have different policies per string substring operation function.

  1. String#slice returns the resolved non-rope string
  2. String#substring returns rope string
  3. String#substr returns rope string in runtime function, non-rope string in DFG and FTL

Due to (3), we see large memory use in the tested web page[1]. Non rope substring have a problem.
First of all, that returned string seems not used immediately. It is possible that the resulted
string is used as a part of the other ropes (like, xxx.substring(...) + "Hello"). To avoid the
eager materialization of the string, we are using StringImpl::createSubstringSharingImpl for the
resulted non rope string. StringImpl::createSubstringSharingImpl is StringImpl's substring feature: the
substring is pointing the owner StringImpl. While this has memory saving benefit, it can retain owner
StringImpl so long, and it could keep very large owner StringImpl alive.

The problem we are attempting to solve with StringImpl::createSubstringSharingImpl can be solved by
the rope string simply. Rope string can share the underlying string. And good feature of the rope
string is that, when resolving rope string, the rope string always create a new StringImpl instead of
using StringImpl::createSubstringSharingImpl. So we allow the owner StringImpl to be destroyed. And this
resolving only happens when we actually want to use the content of the rope string. In addition, we recently
shrunk the sizeof(JSRopeString) from 48 to 32, so JSRopeString is cheap.

In this patch, we change (2) and (3) to (1), using rope String as a result of substring operations.

RAMification and JetStream2 are neutral. The web page[1] shows large memory footprint improvement from 776MB to 681MB.

[1]: https://beta.observablehq.com/@ldgardner/assignment-4-visualizations-and-multiple-views

  • dfg/DFGOperations.cpp:
  • runtime/StringPrototype.cpp:

(JSC::stringProtoFuncSlice):

  • runtime/StringPrototypeInlines.h:

(JSC::stringSlice):

Location:
trunk/Source/JavaScriptCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r245192 r245194  
     12019-05-10  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] String substring operation should return ropes consistently
     4        https://bugs.webkit.org/show_bug.cgi?id=197765
     5        <rdar://problem/37689944>
     6
     7        Reviewed by Michael Saboff.
     8
     9        Currently we have different policies per string substring operation function.
     10
     11            1. String#slice returns the resolved non-rope string
     12            2. String#substring returns rope string
     13            3. String#substr returns rope string in runtime function, non-rope string in DFG and FTL
     14
     15        Due to (3), we see large memory use in the tested web page[1]. Non rope substring have a problem.
     16        First of all, that returned string seems not used immediately. It is possible that the resulted
     17        string is used as a part of the other ropes (like, xxx.substring(...) + "Hello"). To avoid the
     18        eager materialization of the string, we are using StringImpl::createSubstringSharingImpl for the
     19        resulted non rope string. StringImpl::createSubstringSharingImpl is StringImpl's substring feature: the
     20        substring is pointing the owner StringImpl. While this has memory saving benefit, it can retain owner
     21        StringImpl so long, and it could keep very large owner StringImpl alive.
     22
     23        The problem we are attempting to solve with StringImpl::createSubstringSharingImpl can be solved by
     24        the rope string simply. Rope string can share the underlying string. And good feature of the rope
     25        string is that, when resolving rope string, the rope string always create a new StringImpl instead of
     26        using StringImpl::createSubstringSharingImpl. So we allow the owner StringImpl to be destroyed. And this
     27        resolving only happens when we actually want to use the content of the rope string. In addition, we recently
     28        shrunk the sizeof(JSRopeString) from 48 to 32, so JSRopeString is cheap.
     29
     30        In this patch, we change (2) and (3) to (1), using rope String as a result of substring operations.
     31
     32        RAMification and JetStream2 are neutral. The web page[1] shows large memory footprint improvement from 776MB to 681MB.
     33
     34        [1]: https://beta.observablehq.com/@ldgardner/assignment-4-visualizations-and-multiple-views
     35
     36        * dfg/DFGOperations.cpp:
     37        * runtime/StringPrototype.cpp:
     38        (JSC::stringProtoFuncSlice):
     39        * runtime/StringPrototypeInlines.h:
     40        (JSC::stringSlice):
     41
    1422019-05-10  Robin Morisset  <rmorisset@apple.com>
    243
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r245064 r245194  
    21982198    VM& vm = exec->vm();
    21992199    NativeCallFrameTracer tracer(&vm, exec);
    2200     auto scope = DECLARE_THROW_SCOPE(vm);
    2201 
    2202     auto string = jsCast<JSString*>(cell)->value(exec);
    2203     RETURN_IF_EXCEPTION(scope, nullptr);
    2204     return jsSubstring(&vm, string, from, span);
     2200
     2201    return jsSubstring(vm, exec, jsCast<JSString*>(cell), from, span);
    22052202}
    22062203
     
    22092206    VM& vm = exec->vm();
    22102207    NativeCallFrameTracer tracer(&vm, exec);
    2211     auto scope = DECLARE_THROW_SCOPE(vm);
    2212 
    2213     auto string = jsCast<JSString*>(cell)->value(exec);
    2214     RETURN_IF_EXCEPTION(scope, nullptr);
     2208
     2209    JSString* string = asString(cell);
    22152210    static_assert(static_cast<uint64_t>(JSString::MaxLength) <= static_cast<uint64_t>(std::numeric_limits<int32_t>::max()), "");
    2216 
    2217     return stringSlice(vm, WTFMove(string), start, end);
     2211    return stringSlice(exec, vm, string, string->length(), start, end);
    22182212}
    22192213
  • trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp

    r245082 r245194  
    11441144    if (!checkObjectCoercible(thisValue))
    11451145        return throwVMTypeError(exec, scope);
    1146     String s = thisValue.toWTFString(exec);
     1146    JSString* string = thisValue.toString(exec);
    11471147    RETURN_IF_EXCEPTION(scope, encodedJSValue());
    11481148
     
    11501150    JSValue a1 = exec->argument(1);
    11511151
    1152     int len = s.length();
    1153     RELEASE_ASSERT(len >= 0);
     1152    int length = string->length();
     1153    RELEASE_ASSERT(length >= 0);
    11541154
    11551155    // The arg processing is very much like ArrayProtoFunc::Slice
    11561156    double start = a0.toInteger(exec);
    11571157    RETURN_IF_EXCEPTION(scope, encodedJSValue());
    1158     double end = a1.isUndefined() ? len : a1.toInteger(exec);
    1159     RETURN_IF_EXCEPTION(scope, encodedJSValue());
    1160     return JSValue::encode(stringSlice(vm, WTFMove(s), start, end));
     1158    double end = a1.isUndefined() ? length : a1.toInteger(exec);
     1159    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     1160    RELEASE_AND_RETURN(scope, JSValue::encode(stringSlice(exec, vm, string, length, start, end)));
    11611161}
    11621162
  • trunk/Source/JavaScriptCore/runtime/StringPrototypeInlines.h

    r243081 r245194  
    3131
    3232template<typename NumberType>
    33 ALWAYS_INLINE JSString* stringSlice(VM& vm, String&& string, NumberType start, NumberType end)
     33ALWAYS_INLINE JSString* stringSlice(ExecState* exec, VM& vm, JSString* string, int32_t length, NumberType start, NumberType end)
    3434{
    35     int32_t length = string.length();
    3635    NumberType from = start < 0 ? length + start : start;
    3736    NumberType to = end < 0 ? length + end : end;
     
    4140        if (to > length)
    4241            to = length;
    43         return jsSubstring(&vm, WTFMove(string), static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from));
     42        return jsSubstring(vm, exec, string, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from));
    4443    }
    4544    return jsEmptyString(&vm);
Note: See TracChangeset for help on using the changeset viewer.