Add string allocation fast path outside Windows
authorKoundinya Veluri <kouvel@microsoft.com>
Tue, 13 Oct 2015 22:49:03 +0000 (15:49 -0700)
committerKoundinya Veluri <kouvel@microsoft.com>
Tue, 13 Oct 2015 23:19:04 +0000 (16:19 -0700)
- On Linux, a microbenchmark is 140% faster with the portable fast path
- On Windows with the asm fast path, the microbenchmark was 300% faster than on Linux before, and is now 65% faster with the portable fast path
- On Windows, the portable fast path is 5% slower than the asm fast path

src/vm/jithelpers.cpp
src/vm/jitinterface.h
src/vm/jitinterfacegen.cpp

index 12d4458..a1ba4be 100644 (file)
@@ -2988,6 +2988,74 @@ HCIMPLEND
 //
 //========================================================================
 
+#include <optsmallperfcritical.h>
+
+//*************************************************************
+// Allocation fast path for typical objects
+//
+HCIMPL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength)
+{
+    FCALL_CONTRACT;
+
+    do
+    {
+        _ASSERTE(GCHeap::UseAllocationContexts());
+
+        // Instead of doing elaborate overflow checks, we just limit the number of elements. This will avoid all overflow
+        // problems, as well as making sure big string objects are correctly allocated in the big object heap.
+        if (stringLength >= (LARGE_OBJECT_SIZE - 256) / sizeof(WCHAR))
+        {
+            break;
+        }
+
+        // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
+        // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
+        // some reshuffling of intermediate values into nonvolatile registers around the call.
+        Thread *thread = GetThread();
+
+        SIZE_T totalSize = StringObject::GetSize(stringLength);
+
+        // The method table's base size includes space for a terminating null character
+        _ASSERTE(totalSize >= g_pStringClass->GetBaseSize());
+        _ASSERTE((totalSize - g_pStringClass->GetBaseSize()) / sizeof(WCHAR) == stringLength);
+
+        SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
+        _ASSERTE(alignedTotalSize >= totalSize);
+        totalSize = alignedTotalSize;
+
+        alloc_context *allocContext = thread->GetAllocContext();
+        BYTE *allocPtr = allocContext->alloc_ptr;
+        _ASSERTE(allocPtr <= allocContext->alloc_limit);
+        if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
+        {
+            break;
+        }
+        allocContext->alloc_ptr = allocPtr + totalSize;
+
+        _ASSERTE(allocPtr != nullptr);
+        StringObject *stringObject = reinterpret_cast<StringObject *>(allocPtr);
+        stringObject->SetMethodTable(g_pStringClass);
+        stringObject->SetStringLength(stringLength);
+        _ASSERTE(stringObject->GetBuffer()[stringLength] == W('\0'));
+
+#if CHECK_APP_DOMAIN_LEAKS
+        if (g_pConfig->AppDomainLeaks())
+        {
+            stringObject->SetAppDomain();
+        }
+#endif // CHECK_APP_DOMAIN_LEAKS
+
+        return stringObject;
+    } while (false);
+
+    // Tail call to the slow helper
+    ENDFORBIDGC();
+    return HCCALL1(FramedAllocateString, stringLength);
+}
+HCIMPLEND
+
+#include <optdefault.h>
+
 /*********************************************************************/
 /* We don't use HCIMPL macros because this is not a real helper call */
 /* This function just needs mangled arguments like a helper call     */
index 035d164..ad34fa9 100644 (file)
@@ -203,6 +203,10 @@ extern FCDECL1(Object*, JIT_New, CORINFO_CLASS_HANDLE typeHnd_);
 EXTERN_C FCDECL1(Object*, JIT_NewCrossContext, CORINFO_CLASS_HANDLE typeHnd_);
 EXTERN_C FCDECL1(Object*, JIT_NewCrossContext_Portable, CORINFO_CLASS_HANDLE typeHnd_);
 
+extern FCDECL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength);
+extern FCDECL1(StringObject*, UnframedAllocateString, DWORD stringLength);
+extern FCDECL1(StringObject*, FramedAllocateString, DWORD stringLength);
+
 extern FCDECL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_, INT_PTR size);
 extern FCDECL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_, INT_PTR size);
 extern FCDECL2(Object*, JIT_NewArr1, CORINFO_CLASS_HANDLE typeHnd_, INT_PTR size);
@@ -1615,8 +1619,6 @@ void DoGcStress (PT_CONTEXT regs, MethodDesc *pMD);
 #endif //HAVE_GCCOVER
 
 EXTERN_C FCDECL2(LPVOID, ArrayStoreCheck, Object** pElement, PtrArray** pArray);
-FCDECL1(StringObject*, FramedAllocateString, DWORD stringLength);
-FCDECL1(StringObject*, UnframedAllocateString, DWORD stringLength);
 
 OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken metaTok);
 
index 5cf1de9..3c91665 100644 (file)
@@ -229,6 +229,8 @@ void InitJITHelpers1()
             SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable);
             SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable);
             SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
+
+            ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
 #else // !FEATURE_IMPLICIT_TLS
             // If the TLS for Thread is low enough use the super-fast helpers
             if (gThreadTLSIndex < TLS_MINIMUM_AVAILABLE)