OSDN Git Service

Add FatVector
authorChris Craik <ccraik@google.com>
Thu, 29 Oct 2015 19:46:19 +0000 (12:46 -0700)
committerChris Craik <ccraik@google.com>
Thu, 29 Oct 2015 20:22:06 +0000 (13:22 -0700)
FatVector uses an inlined allocation to avoid malloc costs for small
vectors, especially those which are constructed/destructed frequently.

Change-Id: I9809dfd1255cfe98b4ce2b1124ca0fd6ca33dfe0

libs/hwui/Android.mk
libs/hwui/unit_tests/FatVectorTests.cpp [new file with mode: 0644]
libs/hwui/unit_tests/LinearAllocatorTests.cpp
libs/hwui/unit_tests/TestUtils.h
libs/hwui/utils/FatVector.h [new file with mode: 0644]

index f14e444..d94c91d 100644 (file)
@@ -203,6 +203,7 @@ LOCAL_SRC_FILES += \
     unit_tests/CanvasStateTests.cpp \
     unit_tests/ClipAreaTests.cpp \
     unit_tests/DamageAccumulatorTests.cpp \
+    unit_tests/FatVectorTests.cpp \
     unit_tests/LinearAllocatorTests.cpp \
     unit_tests/StringUtilsTests.cpp
 
diff --git a/libs/hwui/unit_tests/FatVectorTests.cpp b/libs/hwui/unit_tests/FatVectorTests.cpp
new file mode 100644 (file)
index 0000000..fb760ac
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <utils/FatVector.h>
+
+#include <unit_tests/TestUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+template<class VectorType>
+static bool allocationIsInternal(VectorType& v) {
+    // allocation array (from &v[0] to &v[0] + v.capacity) is
+    // located within the vector object itself
+    return (char*)(&v) <= (char*)(&v[0])
+            && (char*)(&v + 1) >= (char*)(&v[0] + v.capacity());
+}
+
+TEST(FatVector, baseline) {
+    // Verify allocation behavior FatVector contrasts against - allocations are always external
+    std::vector<int> v;
+    for (int i = 0; i < 50; i++) {
+        v.push_back(i);
+        EXPECT_FALSE(allocationIsInternal(v));
+    }
+}
+
+TEST(FatVector, simpleAllocate) {
+    FatVector<int, 4> v;
+    EXPECT_EQ(4u, v.capacity());
+
+    // can insert 4 items into internal buffer
+    for (int i = 0; i < 4; i++) {
+        v.push_back(i);
+        EXPECT_TRUE(allocationIsInternal(v));
+    }
+
+    // then will fall back to external allocation
+    for (int i = 5; i < 50; i++) {
+        v.push_back(i);
+        EXPECT_FALSE(allocationIsInternal(v));
+    }
+}
+
+TEST(FatVector, shrink) {
+    FatVector<int, 10> v;
+    EXPECT_TRUE(allocationIsInternal(v));
+
+    // push into external alloc
+    v.resize(11);
+    EXPECT_FALSE(allocationIsInternal(v));
+
+    // shrinking back to internal alloc succeeds
+    // note that shrinking further will succeed, but is a waste
+    v.resize(10);
+    v.shrink_to_fit();
+    EXPECT_TRUE(allocationIsInternal(v));
+}
+
+TEST(FatVector, destructorInternal) {
+    int count = 0;
+    {
+        // push 1 into external allocation, verify destruction happens once
+        FatVector<TestUtils::SignalingDtor, 0> v;
+        v.emplace_back(&count);
+        EXPECT_FALSE(allocationIsInternal(v));
+        EXPECT_EQ(0, count);
+    }
+    EXPECT_EQ(1, count);
+}
+
+TEST(FatVector, destructorExternal) {
+    int count = 0;
+    {
+        // push 10 into internal allocation, verify 10 destructors called
+        FatVector<TestUtils::SignalingDtor, 10> v;
+        for (int i = 0; i < 10; i++) {
+            v.emplace_back(&count);
+            EXPECT_TRUE(allocationIsInternal(v));
+        }
+        EXPECT_EQ(0, count);
+    }
+    EXPECT_EQ(10, count);
+}
index 02cd77a..0f6b249 100644 (file)
@@ -17,6 +17,8 @@
 #include <gtest/gtest.h>
 #include <utils/LinearAllocator.h>
 
+#include <unit_tests/TestUtils.h>
+
 using namespace android;
 using namespace android::uirenderer;
 
@@ -25,27 +27,6 @@ struct SimplePair {
     int two = 2;
 };
 
-class SignalingDtor {
-public:
-    SignalingDtor() {
-        mDestroyed = nullptr;
-    }
-    SignalingDtor(bool* destroyedSignal) {
-        mDestroyed = destroyedSignal;
-        *mDestroyed = false;
-    }
-    virtual ~SignalingDtor() {
-        if (mDestroyed) {
-            *mDestroyed = true;
-        }
-    }
-    void setSignal(bool* destroyedSignal) {
-        mDestroyed = destroyedSignal;
-    }
-private:
-    bool* mDestroyed;
-};
-
 TEST(LinearAllocator, alloc) {
     LinearAllocator la;
     EXPECT_EQ(0u, la.usedSize());
@@ -62,31 +43,31 @@ TEST(LinearAllocator, alloc) {
 }
 
 TEST(LinearAllocator, dtor) {
-    bool destroyed[10];
+    int destroyed[10] = { 0 };
     {
         LinearAllocator la;
         for (int i = 0; i < 5; i++) {
-            la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+            la.alloc<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
             la.alloc<SimplePair>();
         }
         la.alloc(100);
         for (int i = 0; i < 5; i++) {
-            auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+            auto sd = new (la) TestUtils::SignalingDtor(destroyed + 5 + i);
             la.autoDestroy(sd);
             new (la) SimplePair();
         }
         la.alloc(100);
         for (int i = 0; i < 10; i++) {
-            EXPECT_FALSE(destroyed[i]);
+            EXPECT_EQ(0, destroyed[i]);
         }
     }
     for (int i = 0; i < 10; i++) {
-        EXPECT_TRUE(destroyed[i]);
+        EXPECT_EQ(1, destroyed[i]);
     }
 }
 
 TEST(LinearAllocator, rewind) {
-    bool destroyed;
+    int destroyed = 0;
     {
         LinearAllocator la;
         auto addr = la.alloc(100);
@@ -94,17 +75,16 @@ TEST(LinearAllocator, rewind) {
         la.rewindIfLastAlloc(addr, 100);
         EXPECT_GT(16u, la.usedSize());
         size_t emptySize = la.usedSize();
-        auto sigdtor = la.alloc<SignalingDtor>();
+        auto sigdtor = la.alloc<TestUtils::SignalingDtor>();
         sigdtor->setSignal(&destroyed);
-        EXPECT_FALSE(destroyed);
+        EXPECT_EQ(0, destroyed);
         EXPECT_LE(emptySize, la.usedSize());
         la.rewindIfLastAlloc(sigdtor);
-        EXPECT_TRUE(destroyed);
+        EXPECT_EQ(1, destroyed);
         EXPECT_EQ(emptySize, la.usedSize());
-        destroyed = false;
     }
     // Checking for a double-destroy case
-    EXPECT_EQ(destroyed, false);
+    EXPECT_EQ(1, destroyed);
 }
 
 TEST(LinearStdAllocator, simpleAllocate) {
index 99ecc9b..5b09fda 100644 (file)
@@ -39,6 +39,24 @@ namespace uirenderer {
 
 class TestUtils {
 public:
+    class SignalingDtor {
+    public:
+        SignalingDtor()
+                : mSignal(nullptr) {}
+        SignalingDtor(int* signal)
+                : mSignal(signal) {}
+        void setSignal(int* signal) {
+            mSignal = signal;
+        }
+        ~SignalingDtor() {
+            if (mSignal) {
+                (*mSignal)++;
+            }
+        }
+    private:
+        int* mSignal;
+    };
+
     static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
         for (int i = 0; i < 16; i++) {
             if (!MathUtils::areEqual(a[i], b[i])) {
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
new file mode 100644 (file)
index 0000000..c3c16c5
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_FAT_VECTOR_H
+#define ANDROID_FAT_VECTOR_H
+
+#include "utils/Macros.h"
+
+#include <stddef.h>
+#include <type_traits>
+#include <utils/Log.h>
+
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+template <typename T, size_t SIZE>
+class InlineStdAllocator {
+public:
+    struct Allocation {
+        PREVENT_COPY_AND_ASSIGN(Allocation);
+    public:
+        Allocation() {};
+        // char array instead of T array, so memory is uninitialized, with no destructors run
+        char array[sizeof(T) * SIZE];
+        bool inUse = false;
+    };
+
+    typedef T value_type; // needed to implement std::allocator
+    typedef T* pointer; // needed to implement std::allocator
+
+    InlineStdAllocator(Allocation& allocation)
+            : mAllocation(allocation) {}
+    InlineStdAllocator(const InlineStdAllocator& other)
+            : mAllocation(other.mAllocation) {}
+    ~InlineStdAllocator() {}
+
+    T* allocate(size_t num, const void* = 0) {
+        if (!mAllocation.inUse && num <= SIZE) {
+            mAllocation.inUse = true;
+            return (T*) mAllocation.array;
+        } else {
+            return (T*) malloc(num * sizeof(T));
+        }
+    }
+
+    void deallocate(pointer p, size_t num) {
+        if (p == (T*)mAllocation.array) {
+            mAllocation.inUse = false;
+        } else {
+            // 'free' instead of delete here - destruction handled separately
+            free(p);
+        }
+    }
+    Allocation& mAllocation;
+};
+
+/**
+ * std::vector with SIZE elements preallocated into an internal buffer.
+ *
+ * Useful for avoiding the cost of malloc in cases where only SIZE or
+ * fewer elements are needed in the common case.
+ */
+template <typename T, size_t SIZE>
+class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
+public:
+    FatVector() : std::vector<T, InlineStdAllocator<T, SIZE>>(
+            InlineStdAllocator<T, SIZE>(mAllocation)) {
+        this->reserve(SIZE);
+    }
+private:
+    typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_FAT_VECTOR_H