Ignore:
Timestamp:
Jun 23, 2015, 7:33:18 PM (10 years ago)
Author:
Darin Adler
Message:

Make Array.join work directly on substrings without reifying them
https://bugs.webkit.org/show_bug.cgi?id=146191

Reviewed by Andreas Kling.

Source/JavaScriptCore:

Besides the Array.join change, this has other optimizations based on
profiling the Peacekeeper array benchmark.

I measured a 14% speed improvement in the Peacekeeper array benchmark.

Still a lot of low hanging fruit in that test because so many of functions
on the array prototype are not optimizing for simple cases. For example,
the reverse function does individual get and put calls even when the array
is entirely made up of integers in contiguous storage.

  • runtime/ArrayPrototype.cpp:

(JSC::getProperty): Use tryGetIndexQuickly first before getPropertySlot.
(JSC::argumentClampedIndexFromStartOrEnd): Marked inline.
(JSC::shift): Use the getProperty helper in this file instead of using
getPropertySlot. Use putByIndexInline instead of calling putByIndex directly.
In both cases this can yield a faster code path.
(JSC::unshift): Ditto.
(JSC::arrayProtoFuncToString): Updated to use the new JSStringJoiner
interface. Changed local variable name to thisArray since it's not a
JSObject*. Changed loop index to i instead of k.
(JSC::arrayProtoFuncToLocaleString): Updated to use the new JSStringJoiner
interface. Renamed thisObj to thisObject. Added a missing exception check
after the toLocaleString function is called, but before toString is called
the result of that function.
(JSC::arrayProtoFuncJoin): Updated to use the new JSStringJointer interface.
Added a missing exception check after calling toString on the separator
but before calling get to get the first element in the array-like object
being joined. Changed loop index to i instead of k. Added missing exception
check after calling toString on each string from the array before calling
get for the next element.
(JSC::arrayProtoFuncConcat): Use JSArray::length instead of using the
getLength function.
(JSC::arrayProtoFuncReverse): Ditto. Also use putByIndexInline.
(JSC::arrayProtoFuncShift): Ditto.
(JSC::arrayProtoFuncSplice): Use getIndex instead of get, which includes some
additional optimizations.
(JSC::getOrHole): Deleted. Unused function.
(JSC::arrayProtoFuncUnShift): Use putByIndexInline.

  • runtime/ExceptionHelpers.cpp:

(JSC::errorDescriptionForValue): Removed the duplicate copy of the the logic
from JSValue::toString.

  • runtime/JSCJSValue.cpp:

(JSC::JSValue::toStringSlowCase): Improved the performance when converting a
small integer to a single character string.
(JSC::JSValue::toWTFStringSlowCase): Moved the contents of the
inlineJSValueNotStringtoString function here.

  • runtime/JSCJSValue.h: Removed no longer used toWTFStringInline and fixed

a comment with a typo.

  • runtime/JSObject.h:

(JSC::JSObject::putByIndexInline): Marked ALWAYS_INLINE because this was not
getting inlined at some call sites.
(JSC::JSObject::indexingData): Deleted. Unused function.
(JSC::JSObject::currentIndexingData): Deleted. Unused function.
(JSC::JSObject::getHolyIndexQuickly): Deleted. Unused function.
(JSC::JSObject::relevantLength): Deleted. Unused function.
(JSC::JSObject::currentRelevantLength): Deleted. Unused function.

  • runtime/JSString.h: Added the StringViewWithUnderlyingString struct and

the viewWithUnderlyingString function. Removed the inlineJSValueNotStringtoString
and toWTFStringInline functions.

  • runtime/JSStringJoiner.cpp:

(JSC::appendStringToData): Changed this to be a template instead of writing
it out, since StringView::getCharactersWithUpconvert does almsot exactly what
this function was trying to do.
(JSC::joinStrings): Rewrote this to use StringView.
(JSC::JSStringJoiner::joinedLength): Added. Factored out from the join function.
(JSC::JSStringJoiner::join): Rewrote to make it a bit simpler. Added an assertion
that we entirely filled capacity, since we are now reserving capacity and using
uncheckedAppend. Use String instead of RefPtr<StringImpl> because there was no
particular value to using the impl directly.

  • runtime/JSStringJoiner.h: Changed the interface to the class to use StringView.

Also changed this class so it now has the responsibility to convert each JSValue
into a string. This let us share more code between toString and join, and also
lets us use the new viewWithUnderlyingString function, which could be confusing at
all the call sites, but is easier to understand here.

Source/WTF:

  • wtf/Vector.h: Added an overload of uncheckedAppend like the one we added

a while back, a non-template function that forwards through to the function
template. This lets us call uncheckedAppend on an argument list and have it
properly convert it to the Vector's element type.

  • wtf/text/StringView.h:

(WTF::StringView::getCharactersWithUpconvert): Changed to not use memcpy;
saw some indication the hand-written loop was faster when profiling. Also
use m_length directly when we know we are dealing with an 8-bit string,
since the masking that the index function does is not needed in that case.
(WTF::StringView::UpconvertedCharacters::UpconvertedCharacters): Ditto.
(WTF::StringView::toString): Ditto.
(WTF::StringView::toFloat): Ditto.
(WTF::StringView::toInt): Ditto.
(WTF::StringView::toStringWithoutCopying): Ditto.
(WTF::StringView::find): Ditto.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/JSString.h

    r185659 r185899  
    6363JSRopeString* jsStringBuilder(VM*);
    6464
     65struct StringViewWithUnderlyingString {
     66    StringView view;
     67    String underlyingString;
     68};
     69
    6570class JSString : public JSCell {
    6671public:
     
    145150    RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const;
    146151    StringView view(ExecState*) const;
     152    StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
    147153    const String& value(ExecState*) const;
    148154    const String& tryGetValue() const;
     
    385391    void clearFibers() const;
    386392    StringView view(ExecState*) const;
     393    StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
    387394
    388395    WriteBarrierBase<JSString>& fiber(unsigned i) const
     
    674681}
    675682
    676 ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
    677 {
    678     VM& vm = exec->vm();
    679     if (value.isInt32())
    680         return vm.numericStrings.add(value.asInt32());
    681     if (value.isDouble())
    682         return vm.numericStrings.add(value.asDouble());
    683     if (value.isTrue())
    684         return vm.propertyNames->trueKeyword.string();
    685     if (value.isFalse())
    686         return vm.propertyNames->falseKeyword.string();
    687     if (value.isNull())
    688         return vm.propertyNames->nullKeyword.string();
    689     if (value.isUndefined())
    690         return vm.propertyNames->undefinedKeyword.string();
    691     return value.toString(exec)->value(exec);
    692 }
    693 
    694 ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
    695 {
    696     if (isString())
    697         return static_cast<JSString*>(asCell())->value(exec);
    698 
    699     return inlineJSValueNotStringtoString(*this, exec);
    700 }
    701 
    702683ALWAYS_INLINE StringView JSRopeString::view(ExecState* exec) const
    703684{
     
    711692}
    712693
     694ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState& state) const
     695{
     696    if (isSubstring()) {
     697        auto& base = substringBase()->m_value;
     698        if (is8Bit())
     699            return { { base.characters8() + substringOffset(), m_length }, base };
     700        return { { base.characters16() + substringOffset(), m_length }, base };
     701    }
     702    resolveRope(&state);
     703    return { m_value, m_value };
     704}
     705
    713706ALWAYS_INLINE StringView JSString::view(ExecState* exec) const
    714707{
     
    718711}
    719712
     713ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState& state) const
     714{
     715    if (isRope())
     716        return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(state);
     717    return { m_value, m_value };
     718}
     719
    720720inline bool JSString::isSubstring() const
    721721{
Note: See TracChangeset for help on using the changeset viewer.