2
0
Эх сурвалжийг харах

NumberToString: Improve performance and reduce cache memory

- NumberToString ~100% faster on xplat and ~50% faster on Windows
- Reduce cache size to 512 JsString from 1024
- introduce new definition `CC_LOW_MEMORY_TARGET` to prevent caching on
low memory targets
- Add Todo note for making cache more effective on long running apps.
Oguz Bastemur 8 жил өмнө
parent
commit
1468c917d3

+ 16 - 4
lib/Runtime/Base/ScriptContext.cpp

@@ -1733,25 +1733,37 @@ namespace Js
 
         JavascriptString *string;
 
+// TODO: (obastemur) Could this be dynamic instead of compile time?
+#ifndef CC_LOW_MEMORY_TARGET // we don't need this on a target with low memory
         if (!this->integerStringMap.TryGetValue(value, &string))
         {
             // Add the string to hash table cache
             // Don't add if table is getting too full.  We'll be holding on to
             // too many strings, and table lookup will become too slow.
-            if (this->integerStringMap.Count() > 1024)
+            // TODO: Long term running app, this cache doesn't provide much value?
+            //       i.e. what is the importance of first 512 number to string calls?
+            //       a solution; count the number of times we couldn't use cache
+            //       after cache is full. If it's bigger than X ?? the discard the
+            //       previous cache?
+            if (this->integerStringMap.Count() > 512)
             {
+#endif
                 // Use recycler memory
                 string = TaggedInt::ToString(value, this);
+
+#ifndef CC_LOW_MEMORY_TARGET
             }
             else
             {
-                char16 stringBuffer[20];
+                char16 stringBuffer[22];
 
-                TaggedInt::ToBuffer(value, stringBuffer, _countof(stringBuffer));
-                string = JavascriptString::NewCopySzFromArena(stringBuffer, this, this->GeneralAllocator());
+                int pos = TaggedInt::ToBuffer(value, stringBuffer, _countof(stringBuffer));
+                string = JavascriptString::NewCopySzFromArena(stringBuffer + pos,
+                    this, this->GeneralAllocator(), (_countof(stringBuffer) - 1) - pos);
                 this->integerStringMap.AddNew(value, string);
             }
         }
+#endif
 
         return string;
     }

+ 59 - 13
lib/Runtime/Language/TaggedInt.cpp

@@ -428,21 +428,66 @@ LblDone:
         return JavascriptNumber::ToVar(uValue >> (nShift & 0x1F), scriptContext);
     }
 
-    void TaggedInt::ToBuffer(Var aValue, __out_ecount_z(bufSize) char16 * buffer, uint bufSize)
+    int TaggedInt::ToBuffer(Var aValue, __out_ecount_z(bufSize) char16 * buffer, uint bufSize)
     {
         return ToBuffer(ToInt32(aValue), buffer, bufSize);
     }
 
-    void TaggedInt::ToBuffer(int value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize)
+    // fills the buffer from the end and returns the start index
+    static int UnsignedToString(unsigned long value, char16 *buffer, int bufferSize)
     {
-        Assert(bufSize > 10);
-        _itow_s(value, buffer, bufSize, 10);
+        static_assert(sizeof(unsigned long) <= 8, "This method may not support the target architecture");
+        AssertMsg(bufferSize >= 22, "Error: bufferSize is too small. value may not be represented properly");
+
+        buffer[bufferSize - 1] = char16(0);
+        int pos = bufferSize - 2;
+        while(value > 9)
+        {
+            const int val100 = value % 100;
+            value /= 100;
+
+            if (val100 < 10)
+            {
+                buffer[pos--] = _u('0') + static_cast<char>(val100);
+                buffer[pos--] = _u('0');
+                continue;
+            }
+
+            buffer[pos--] = _u('0') + static_cast<char>(val100 % 10);
+            buffer[pos--] = _u('0') + static_cast<char>(val100 / 10);
+        }
+
+        if (value && value < 10)
+        {
+            buffer[pos--] = _u('0') + static_cast<char>(value);
+        }
+        else if (pos == bufferSize - 2) // if it was 0
+        {
+            buffer[pos--] = _u('0');
+        }
+
+        return pos + 1;
+    }
+
+    static int SignedToString(long value, char16 *buffer, int bufferSize)
+    {
+        bool neg = value < 0;
+        unsigned long val = (unsigned long) (neg ? -1 * value : value);
+        int pos = UnsignedToString(val, buffer, bufferSize);
+        if (neg) buffer[--pos] = _u('-');
+        return pos;
     }
 
-    void TaggedInt::ToBuffer(uint value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize)
+    int TaggedInt::ToBuffer(int value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize)
     {
-        Assert(bufSize > 10);
-        _ultow_s(value, buffer, bufSize, 10);
+        Assert(bufSize >= 22);
+        return SignedToString(value, buffer, bufSize);
+    }
+
+    int TaggedInt::ToBuffer(uint value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize)
+    {
+        Assert(bufSize >= 22);
+        return UnsignedToString(value, buffer, bufSize);
     }
 
     JavascriptString* TaggedInt::ToString(Var aValue,ScriptContext* scriptContext)
@@ -452,17 +497,18 @@ LblDone:
 
     JavascriptString* TaggedInt::ToString(int value, ScriptContext* scriptContext)
     {
-        char16 szBuffer[20];
-        ToBuffer(value, szBuffer, _countof(szBuffer));
+        char16 szBuffer[22];
+        int pos = ToBuffer(value, szBuffer, _countof(szBuffer));
 
-        return JavascriptString::NewCopySz(szBuffer, scriptContext);
+        return JavascriptString::NewCopyBuffer(szBuffer + pos, (_countof(szBuffer) - 1) - pos, scriptContext);
     }
+
     JavascriptString* TaggedInt::ToString(uint value, ScriptContext* scriptContext)
     {
-        char16 szBuffer[20];
-        ToBuffer(value, szBuffer, _countof(szBuffer));
+        char16 szBuffer[22];
+        int pos = ToBuffer(value, szBuffer, _countof(szBuffer));
 
-        return JavascriptString::NewCopySz(szBuffer, scriptContext);
+        return JavascriptString::NewCopyBuffer(szBuffer + pos, (_countof(szBuffer) - 1) - pos, scriptContext);
     }
 
     Var TaggedInt::NegateUnchecked(Var aValue)

+ 3 - 3
lib/Runtime/Language/TaggedInt.h

@@ -51,9 +51,9 @@ namespace Js {
         static int64 ToInt64(Var aValue);
         static uint16 ToUInt16(Var aValue);
         static Var ToVarUnchecked(int nValue);
-        static void ToBuffer(Var aValue, __out_ecount_z(bufSize) char16 * buffer, uint bufSize);
-        static void ToBuffer(int value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize);
-        static void ToBuffer(uint value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize);
+        static int ToBuffer(Var aValue, __out_ecount_z(bufSize) char16 * buffer, uint bufSize);
+        static int ToBuffer(int value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize);
+        static int ToBuffer(uint value, __out_ecount_z(bufSize) char16 * buffer, uint bufSize);
         static JavascriptString* ToString(Var aValue,ScriptContext* scriptContext);
         static JavascriptString* ToString(int value,ScriptContext* scriptContext);
         static JavascriptString* ToString(uint value,ScriptContext* scriptContext);

+ 7 - 2
lib/Runtime/Library/JavascriptString.cpp

@@ -128,11 +128,16 @@ namespace Js
         return NewWithBufferT<LiteralString, true>(content, cchUseLength, scriptContext);
     }
 
-    JavascriptString* JavascriptString::NewCopySzFromArena(__in_z const char16* content, ScriptContext* scriptContext, ArenaAllocator *arena)
+    JavascriptString* JavascriptString::NewCopySzFromArena(__in_z const char16* content,
+        ScriptContext* scriptContext, ArenaAllocator *arena, charcount_t cchUseLength)
     {
         AssertMsg(content != nullptr, "NULL value passed to JavascriptString::New");
 
-        charcount_t cchUseLength = JavascriptString::GetBufferLength(content);
+        if (!cchUseLength)
+        {
+            cchUseLength = JavascriptString::GetBufferLength(content);
+        }
+
         char16* buffer = JavascriptString::AllocateAndCopySz(arena, content, cchUseLength);
         return ArenaLiteralString::New(scriptContext->GetLibrary()->GetStringTypeStatic(),
             buffer, cchUseLength, arena);

+ 1 - 1
lib/Runtime/Library/JavascriptString.h

@@ -194,7 +194,7 @@ namespace Js
         static JavascriptString* NewWithArenaSz(__in_z const char16 * content, ScriptContext* scriptContext);
         static JavascriptString* NewWithArenaBuffer(__in_ecount(charLength) const char16 * content, charcount_t charLength, ScriptContext * scriptContext);
 
-        static JavascriptString* NewCopySzFromArena(__in_z const char16* content, ScriptContext* scriptContext, ArenaAllocator *arena);
+        static JavascriptString* NewCopySzFromArena(__in_z const char16* content, ScriptContext* scriptContext, ArenaAllocator *arena, charcount_t cchUseLength = 0);
 
         static __ecount(length+1) char16* AllocateLeafAndCopySz(__in Recycler* recycler, __in_ecount(length) const char16* content, charcount_t length);
         static __ecount(length+1) char16* AllocateAndCopySz(__in ArenaAllocator* arena, __in_ecount(length) const char16* content, charcount_t length);