Use ptr diff to encode function and padding.
authorHerb Derby <herb@google.com>
Mon, 21 Nov 2016 17:09:27 +0000 (12:09 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 21 Nov 2016 17:41:43 +0000 (17:41 +0000)
This drops allocation overhead from 16 bytes down to 4 bytes.

BUG=skia:

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=5066

CQ_INCLUDE_TRYBOTS=master.client.skia:Test-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Debug-Trybot;master.client.skia:Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-GN-Trybot

Change-Id: Id4665fd914dfb679996cefaf36fce6ef225fafb5
Reviewed-on: https://skia-review.googlesource.com/5066
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Herb Derby <herb@google.com>

src/core/SkFixedAlloc.cpp
src/core/SkFixedAlloc.h

index 133245a..cb2f798 100644 (file)
@@ -8,28 +8,29 @@
 #include "SkFixedAlloc.h"
 
 SkFixedAlloc::SkFixedAlloc(void* ptr, size_t len)
-    : fBuffer((char*)ptr), fUsed(0), fLimit(len) {}
+    : fStorage((char*)ptr), fCursor(fStorage), fEnd(fStorage + len) {}
 
 void SkFixedAlloc::undo() {
     // This function is essentially make() in reverse.
 
     // First, read the Footer we stamped at the end.
     Footer footer;
-    memcpy(&footer, fBuffer + fUsed - sizeof(Footer), sizeof(Footer));
+    memcpy(&footer, fCursor - sizeof(Footer), sizeof(Footer));
 
-    // That tells us where the T starts and how to destroy it.
-    footer.dtor(fBuffer + fUsed - sizeof(Footer) - footer.len);
+    Releaser releaser = (Releaser)((char*)Base + (footer >> 5));
+    ptrdiff_t padding = footer & 31;
 
-    // We can reuse bytes that stored the Footer, the T, and any that we skipped for alignment.
-    fUsed -= sizeof(Footer) + footer.len + footer.skip;
+    fCursor = releaser(fCursor);
+    fCursor -= padding;
 }
 
 void SkFixedAlloc::reset() {
-    while (fUsed) {
+    while (fCursor > fStorage) {
         this->undo();
     }
 }
 
+void SkFixedAlloc::Base() { }
 
 SkFallbackAlloc::SkFallbackAlloc(SkFixedAlloc* fixed) : fFixedAlloc(fixed) {}
 
index cbf5ac2..4d148f9 100644 (file)
@@ -21,31 +21,47 @@ public:
     ~SkFixedAlloc() { this->reset(); }
 
     // Allocates a new T in the buffer if possible.  If not, returns nullptr.
+    // Assumptions:
+    // * max alignment value is 32 - if alignment is greater than 32, the allocation is best effort.
+    // * footer is 32 bits - 5 bits of alignment and 27 bits of deleter difference from Base.
+    // * deleter difference - the difference D is -2^26 <= D < 2^26.
     template <typename T, typename... Args>
     T* make(Args&&... args) {
-        auto aligned = ((uintptr_t)(fBuffer+fUsed) + alignof(T) - 1) & ~(alignof(T)-1);
-        size_t skip = aligned - (uintptr_t)(fBuffer+fUsed);
+        auto mask = alignof(T) - 1;
 
-        if (!SkTFitsIn<uint32_t>(skip)      ||
-            !SkTFitsIn<uint32_t>(sizeof(T)) ||
-            fUsed + skip + sizeof(T) + sizeof(Footer) > fLimit) {
+        // Align fCursor for this allocation.
+        char* objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
+        ptrdiff_t padding = objStart - fCursor;
+        Releaser releaser = [](char* objEnd) {
+            char* objStart = objEnd - (sizeof(T) + sizeof(Footer));
+            ((T*)objStart)->~T();
+            return objStart;
+        };
+
+        ptrdiff_t deleterDiff = (char*)releaser - (char*)Base;
+
+        // TODO: combine both if statments when study is done.
+        if (objStart + sizeof(T) + sizeof(Footer) > fEnd) {
             return nullptr;
         }
 
-        // Skip ahead until our buffer is aligned for T.
-        fUsed += skip;
+        if (padding >= 32
+            || deleterDiff >= (1 << 26)
+            || deleterDiff < -(1 << 26)) {
+            // Can't encode padding or deleter function offset.
+            SkDebugf("SkFixedAlloc - padding: %dt, deleteDiff: %dt\n", padding, deleterDiff);
+            SkFAIL("Failed to allocate due to constraint.");
+            return nullptr;
+        }
 
-        // Make space for T.
-        void* ptr = fBuffer+fUsed;
-        fUsed += sizeof(T);
+        // Advance cursor to end of the object.
+        fCursor = objStart + sizeof(T);
 
-        // Stamp a footer after the T that we can use to clean it up.
-        Footer footer = { [](void* ptr) { ((T*)ptr)->~T(); }, SkToU32(skip), SkToU32(sizeof(T)) };
-        memcpy(fBuffer+fUsed, &footer, sizeof(Footer));
-        fUsed += sizeof(Footer);
+        Footer footer = (Footer)(SkLeftShift((int64_t)deleterDiff, 5) | padding);
+        memcpy(fCursor, &footer, sizeof(Footer));
+        fCursor += sizeof(Footer);
 
-        // Creating a T must be last for nesting to work.
-        return new (ptr) T(std::forward<Args>(args)...);
+        return new (objStart) T(std::forward<Args>(args)...);
     }
 
     // Destroys the last object allocated and frees its space in the buffer.
@@ -55,13 +71,15 @@ public:
     void reset();
 
 private:
-    struct Footer {
-        void (*dtor)(void*);
-        uint32_t skip, len;
-    };
+    using Footer  = int32_t;
+    using Releaser = char*(*)(char*);
+
+    // A function pointer to use for offsets of releasers.
+    static void Base();
 
-    char* fBuffer;
-    size_t fUsed, fLimit;
+    char* const fStorage;
+    char*       fCursor;
+    char* const fEnd;
 };
 
 class SkFallbackAlloc {