Commit 6261f1f0 authored by antti@apple.com's avatar antti@apple.com

https://bugs.webkit.org/show_bug.cgi?id=50412

http://www.wunderground.com/US/CA/Hayward.html causes big memory spike during page loading 
        
Reviewed by Gavin Barraclough.

Creating a substring caused the original string be flattened if it was in the rope form. This could use
significant amount of memory by reducing buffer sharing between strings.
        
Add a rope specific substring function that constructs the substring by reusing the rope fibers
instead of flattening the rope.
        
No change observed in SunSpider.

* runtime/JSString.cpp:
(JSC::JSString::substringFromRope):
* runtime/JSString.h:
(JSC::jsSubstring):
* runtime/StringPrototype.cpp:
(JSC::stringProtoFuncSubstr):
(JSC::stringProtoFuncSubstring):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@73433 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent e2565259
2010-12-07 Antti Koivisto <antti@apple.com>
Reviewed by Gavin Barraclough.
https://bugs.webkit.org/show_bug.cgi?id=50412
http://www.wunderground.com/US/CA/Hayward.html causes big memory spike during page loading
Creating a substring caused the original string be flattened if it was in the rope form. This could use
significant amount of memory by reducing buffer sharing between strings.
Add a rope specific substring function that constructs the substring by reusing the rope fibers
instead of flattening the rope.
No change observed in SunSpider.
* runtime/JSString.cpp:
(JSC::JSString::substringFromRope):
* runtime/JSString.h:
(JSC::jsSubstring):
* runtime/StringPrototype.cpp:
(JSC::stringProtoFuncSubstr):
(JSC::stringProtoFuncSubstring):
2010-12-06 Geoffrey Garen <ggaren@apple.com>
Reviewed by Gavin Barraclough.
......
......@@ -31,6 +31,8 @@
#include "StringPrototype.h"
namespace JSC {
static const unsigned substringFromRopeCutoff = 4;
// Overview: this methods converts a JSString from holding a string in rope form
// down to a simple UString representation. It does so by building up the string
......@@ -105,6 +107,60 @@ void JSString::resolveRope(ExecState* exec) const
}
}
}
// This function construsts a substring out of a rope without flattening by reusing the existing fibers.
// This can reduce memory usage substantially. Since traversing ropes is slow the function will revert
// back to flattening if the rope turns out to be long.
JSString* JSString::substringFromRope(ExecState* exec, unsigned substringStart, unsigned substringLength)
{
ASSERT(isRope());
ASSERT(substringLength);
JSGlobalData* globalData = &exec->globalData();
UString substringFibers[3];
unsigned fiberCount = 0;
unsigned substringFiberCount = 0;
unsigned substringEnd = substringStart + substringLength;
unsigned fiberEnd = 0;
RopeIterator end;
for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) {
++fiberCount;
StringImpl* fiberString = *it;
unsigned fiberStart = fiberEnd;
fiberEnd = fiberStart + fiberString->length();
if (fiberEnd <= substringStart)
continue;
unsigned copyStart = std::max(substringStart, fiberStart);
unsigned copyEnd = std::min(substringEnd, fiberEnd);
if (copyStart == fiberStart && copyEnd == fiberEnd)
substringFibers[substringFiberCount++] = UString(fiberString);
else
substringFibers[substringFiberCount++] = UString(StringImpl::create(fiberString, copyStart - fiberStart, copyEnd - copyStart));
if (fiberEnd >= substringEnd)
break;
if (fiberCount > substringFromRopeCutoff || substringFiberCount >= 3) {
// This turned out to be a really inefficient rope. Just flatten it.
resolveRope(exec);
return jsSubstring(&exec->globalData(), m_value, substringStart, substringLength);
}
}
ASSERT(substringFiberCount && substringFiberCount <= 3);
if (substringLength == 1) {
ASSERT(substringFiberCount == 1);
UChar c = substringFibers[0].characters()[0];
if (c <= 0xFF)
return globalData->smallStrings.singleCharacterString(globalData, c);
}
if (substringFiberCount == 1)
return new (globalData) JSString(globalData, substringFibers[0]);
if (substringFiberCount == 2)
return new (globalData) JSString(globalData, substringFibers[0], substringFibers[1]);
return new (globalData) JSString(globalData, substringFibers[0], substringFibers[1], substringFibers[2]);
}
JSValue JSString::replaceCharacter(ExecState* exec, UChar character, const UString& replacement)
{
......
......@@ -356,6 +356,7 @@ namespace JSC {
}
void resolveRope(ExecState*) const;
JSString* substringFromRope(ExecState*, unsigned offset, unsigned length);
void appendStringInConstruct(unsigned& index, const UString& string)
{
......@@ -435,6 +436,7 @@ namespace JSC {
friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
friend JSValue jsString(ExecState* exec, JSValue thisValue);
friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
friend JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length);
};
JSString* asString(JSValue);
......@@ -519,6 +521,19 @@ namespace JSC {
JSGlobalData* globalData = &exec->globalData();
return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context));
}
inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
{
ASSERT(offset <= static_cast<unsigned>(s->length()));
ASSERT(length <= static_cast<unsigned>(s->length()));
ASSERT(offset + length <= static_cast<unsigned>(s->length()));
JSGlobalData* globalData = &exec->globalData();
if (!length)
return globalData->smallStrings.emptyString(globalData);
if (s->isRope())
return s->substringFromRope(exec, offset, length);
return jsSubstring(globalData, s->m_value, offset, length);
}
inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
{
......
......@@ -772,8 +772,16 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
JSValue thisValue = exec->hostThisValue();
if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
return throwVMTypeError(exec);
UString s = thisValue.toThisString(exec);
int len = s.length();
unsigned len;
JSString* jsString = 0;
UString uString;
if (thisValue.isString()) {
jsString = static_cast<JSString*>(thisValue.asCell());
len = jsString->length();
} else {
uString = thisValue.toThisObject(exec)->toString(exec);
len = uString.length();
}
JSValue a0 = exec->argument(0);
JSValue a1 = exec->argument(1);
......@@ -789,7 +797,11 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
}
if (start + length > len)
length = len - start;
return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(length)));
unsigned substringStart = static_cast<unsigned>(start);
unsigned substringLength = static_cast<unsigned>(length);
if (jsString)
return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
}
EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
......@@ -797,8 +809,16 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
JSValue thisValue = exec->hostThisValue();
if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
return throwVMTypeError(exec);
UString s = thisValue.toThisString(exec);
int len = s.length();
int len;
JSString* jsString = 0;
UString uString;
if (thisValue.isString()) {
jsString = static_cast<JSString*>(thisValue.asCell());
len = jsString->length();
} else {
uString = thisValue.toThisObject(exec)->toString(exec);
len = uString.length();
}
JSValue a0 = exec->argument(0);
JSValue a1 = exec->argument(1);
......@@ -823,7 +843,11 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
end = start;
start = temp;
}
return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(end) - static_cast<unsigned>(start)));
unsigned substringStart = static_cast<unsigned>(start);
unsigned substringLength = static_cast<unsigned>(end) - substringStart;
if (jsString)
return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
}
EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment