change SkChunkAlloc to grow its allocations geometrically (not linearly)
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 17 May 2012 14:28:11 +0000 (14:28 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 17 May 2012 14:28:11 +0000 (14:28 +0000)
plus add a bench and unittest for it.

git-svn-id: http://skia.googlecode.com/svn/trunk@3989 2bbb7eff-a529-9590-31e7-b0007b416f81

bench/MemoryBench.cpp [new file with mode: 0644]
gyp/bench.gypi
include/core/SkChunkAlloc.h
src/core/SkChunkAlloc.cpp
tests/MemsetTest.cpp

diff --git a/bench/MemoryBench.cpp b/bench/MemoryBench.cpp
new file mode 100644 (file)
index 0000000..e732687
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkChunkAlloc.h"
+#include "SkString.h"
+
+class ChunkAllocBench : public SkBenchmark {
+    SkString    fName;
+    size_t      fMinSize;
+    
+    enum {
+        N = SkBENCHLOOP(1000)
+    };
+public:
+    ChunkAllocBench(void* param, size_t minSize) : INHERITED(param) {
+        fMinSize = minSize;
+        fName.printf("chunkalloc_%d", minSize);
+    }
+    
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        size_t inc = fMinSize >> 4;
+        SkASSERT(inc > 0);
+        size_t total = fMinSize * 64;
+
+        SkChunkAlloc alloc(fMinSize);
+
+        for (int i = 0; i < N; ++i) {
+            size_t size = 0;
+            int calls = 0;
+            while (size < total) {
+                alloc.allocThrow(inc);
+                size += inc;
+                calls += 1;
+            }
+            alloc.reset();
+        }
+    }
+    
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* F0(void* p) { return new ChunkAllocBench(p, 64); }
+static SkBenchmark* F1(void* p) { return new ChunkAllocBench(p, 8*1024); }
+
+static BenchRegistry gR0(F0);
+static BenchRegistry gR1(F1);
+
index 221faed..0489ac6 100644 (file)
@@ -28,6 +28,7 @@
     '../bench/InterpBench.cpp',
     '../bench/MathBench.cpp',
     '../bench/MatrixBench.cpp',
+    '../bench/MemoryBench.cpp',
     '../bench/MutexBench.cpp',
     '../bench/PathBench.cpp',
     '../bench/PathIterBench.cpp',
index 28d3c7e..102962b 100644 (file)
@@ -17,18 +17,12 @@ public:
     SkChunkAlloc(size_t minSize);
     ~SkChunkAlloc();
 
-    /** Free up all allocated blocks. This invalidates all returned
-        pointers.
-    */
+    /**
+     *  Free up all allocated blocks. This invalidates all returned
+     *  pointers.
+     */
     void reset();
 
-    /** Reuse all allocated blocks. This invalidates all returned
-        pointers (like reset) but doesn't necessarily free up all
-        of the privately allocated blocks. This is more efficient
-        if you plan to reuse the allocator multiple times.
-    */
-    void reuse();
-
     enum AllocFailType {
         kReturnNil_AllocFailType,
         kThrow_AllocFailType
@@ -48,6 +42,7 @@ public:
     size_t unalloc(void* ptr);
     
     size_t totalCapacity() const { return fTotalCapacity; }
+    int blockCount() const { return fBlockCount; }
 
     /**
      *  Returns true if the specified address is within one of the chunks, and
@@ -58,10 +53,12 @@ public:
 
 private:
     struct Block;
+
     Block*  fBlock;
     size_t  fMinSize;
-    Block*  fPool;
+    size_t  fChunkSize;
     size_t  fTotalCapacity;
+    int     fBlockCount;
 
     Block* newBlock(size_t bytes, AllocFailType ftype);
 };
index 56b4abe..8df38cb 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,9 +5,18 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkChunkAlloc.h"
 
+// Don't malloc any chunks smaller than this
+#define MIN_CHUNKALLOC_BLOCK_SIZE   1024
+
+// Return the new min blocksize given the current value
+static size_t increase_next_size(size_t size) {
+    return size + (size >> 1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 struct SkChunkAlloc::Block {
     Block*  fNext;
     size_t  fFreeSize;
@@ -19,8 +27,7 @@ struct SkChunkAlloc::Block {
         return reinterpret_cast<char*>(this + 1);
     }
 
-    void freeChain() {    // this can be null
-        Block* block = this;
+    static void FreeChain(Block* block) {
         while (block) {
             Block* next = block->fNext;
             sk_free(block);
@@ -28,29 +35,24 @@ struct SkChunkAlloc::Block {
         }
     };
     
-    Block* tail() {
-        Block* block = this;
-        if (block) {
-            for (;;) {
-                Block* next = block->fNext;
-                if (NULL == next) {
-                    break;
-                }
-                block = next;
-            }
-        }
-        return block;
-    }
-
     bool contains(const void* addr) const {
         const char* ptr = reinterpret_cast<const char*>(addr);
         return ptr >= (const char*)(this + 1) && ptr < fFreePtr;
     }
 };
 
-SkChunkAlloc::SkChunkAlloc(size_t minSize)
-    : fBlock(NULL), fMinSize(SkAlign4(minSize)), fPool(NULL), fTotalCapacity(0)
-{
+///////////////////////////////////////////////////////////////////////////////
+
+SkChunkAlloc::SkChunkAlloc(size_t minSize) {
+    if (minSize < MIN_CHUNKALLOC_BLOCK_SIZE) {
+        minSize = MIN_CHUNKALLOC_BLOCK_SIZE;
+    }
+
+    fBlock = NULL;
+    fMinSize = minSize;
+    fChunkSize = fMinSize;
+    fTotalCapacity = 0;
+    fBlockCount = 0;
 }
 
 SkChunkAlloc::~SkChunkAlloc() {
@@ -58,35 +60,20 @@ SkChunkAlloc::~SkChunkAlloc() {
 }
 
 void SkChunkAlloc::reset() {
-    fBlock->freeChain();
-    fBlock = NULL;
-    fPool->freeChain();
-    fPool = NULL;
-    fTotalCapacity = 0;
-}
-
-void SkChunkAlloc::reuse() {
-    if (fPool && fBlock) {
-        fPool->tail()->fNext = fBlock;
-    }
-    fPool = fBlock;
+    Block::FreeChain(fBlock);
     fBlock = NULL;
+    fChunkSize = fMinSize;  // reset to our initial minSize
     fTotalCapacity = 0;
+    fBlockCount = 0;
 }
 
 SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
-    Block* block = fPool;
-
-    if (block && bytes <= block->fFreeSize) {
-        fPool = block->fNext;
-        return block;
-    }
-
     size_t size = bytes;
-    if (size < fMinSize)
-        size = fMinSize;
+    if (size < fChunkSize) {
+        size = fChunkSize;
+    }
 
-    block = (Block*)sk_malloc_flags(sizeof(Block) + size,
+    Block* block = (Block*)sk_malloc_flags(sizeof(Block) + size,
                         ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0);
 
     if (block) {
@@ -95,6 +82,9 @@ SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
         block->fFreePtr = block->startOfData();
         
         fTotalCapacity += size;
+        fBlockCount += 1;
+        
+        fChunkSize = increase_next_size(fChunkSize);
     }
     return block;
 }
@@ -114,10 +104,10 @@ void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) {
     }
 
     SkASSERT(block && bytes <= block->fFreeSize);
-    void* ptr = block->fFreePtr;
+    char* ptr = block->fFreePtr;
 
     block->fFreeSize -= bytes;
-    block->fFreePtr += bytes;
+    block->fFreePtr = ptr + bytes;
     return ptr;
 }
 
index 9c1fc92..7e80b57 100644 (file)
@@ -1,13 +1,39 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "Test.h"
+#include "SkChunkAlloc.h"
 #include "SkUtils.h"
 
+static void test_chunkalloc(skiatest::Reporter* reporter) {
+    size_t min = 256;
+    SkChunkAlloc alloc(min);
+    
+    REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
+    REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
+    REPORTER_ASSERT(reporter, !alloc.contains(NULL));
+    REPORTER_ASSERT(reporter, !alloc.contains(reporter));
+
+    alloc.reset();
+    REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
+    REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
+
+    size_t size = min >> 1;
+    void* ptr = alloc.allocThrow(size);
+    REPORTER_ASSERT(reporter, alloc.totalCapacity() >= size);
+    REPORTER_ASSERT(reporter, alloc.blockCount() > 0);
+    REPORTER_ASSERT(reporter, alloc.contains(ptr));
+    
+    alloc.reset();
+    REPORTER_ASSERT(reporter, !alloc.contains(ptr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 static void set_zero(void* dst, size_t bytes) {
     char* ptr = (char*)dst;
     for (size_t i = 0; i < bytes; ++i) {
@@ -85,6 +111,8 @@ static void test_32(skiatest::Reporter* reporter) {
 static void TestMemset(skiatest::Reporter* reporter) {
     test_16(reporter);
     test_32(reporter);
+    
+    test_chunkalloc(reporter);
 };
 
 #include "TestClassDef.h"