Add instrumentation into SkArenaAlloc.
Add a parameter to the constructor that will have the dtor print out
stats for setting the initial parameters.
Clean up: Move some function so they are in the same order as .h
Change-Id: I19d87dcc9c3b8dcc3e1d4f2ff078b78bbc490d92
Reviewed-on: https://skia-review.googlesource.com/14600
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/core/SkArenaAlloc.cpp b/src/core/SkArenaAlloc.cpp
index eca3aa9..bfe3ff4 100644
--- a/src/core/SkArenaAlloc.cpp
+++ b/src/core/SkArenaAlloc.cpp
@@ -11,6 +11,64 @@
static char* end_chain(char*) { return nullptr; }
+SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize, Tracking tracking)
+ : fDtorCursor {block}
+ , fCursor {block}
+ , fEnd {block + SkTo<uint32_t>(size)}
+ , fFirstBlock {block}
+ , fFirstSize {SkTo<uint32_t>(size)}
+ , fExtraSize {SkTo<uint32_t>(extraSize)}
+{
+ if (size < sizeof(Footer)) {
+ fEnd = fCursor = fDtorCursor = nullptr;
+ }
+
+ if (tracking == kTrack) {
+ fTotalSlop = 0;
+ }
+
+ if (fCursor != nullptr) {
+ this->installFooter(end_chain, 0);
+ if (fTotalSlop >= 0) {
+ fTotalAlloc += fFirstSize;
+ }
+ }
+}
+
+SkArenaAlloc::~SkArenaAlloc() {
+ if (fTotalSlop >= 0) {
+ int32_t lastSlop = fEnd - fCursor;
+ fTotalSlop += lastSlop;
+ SkDebugf("SkArenaAlloc initial: %p %u %u total alloc: %u total slop: %d last slop: %d\n",
+ fFirstBlock, fFirstSize, fExtraSize, fTotalAlloc, fTotalSlop, lastSlop);
+ }
+ RunDtorsOnBlock(fDtorCursor);
+}
+
+void SkArenaAlloc::reset() {
+ this->~SkArenaAlloc();
+ new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize,
+ fTotalSlop < 0 ? kDontTrack : kTrack};
+}
+
+void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
+ SkASSERT(padding < 64);
+ int64_t actionInt = (int64_t)(intptr_t)action;
+
+ // The top 14 bits should be either all 0s or all 1s. Check this.
+ SkASSERT((actionInt << 6) >> 6 == actionInt);
+ Footer encodedFooter = (actionInt << 6) | padding;
+ memmove(fCursor, &encodedFooter, sizeof(Footer));
+ fCursor += sizeof(Footer);
+ fDtorCursor = fCursor;
+}
+
+void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
+ memmove(fCursor, &ptr, sizeof(char*));
+ fCursor += sizeof(char*);
+ this->installFooter(action, padding);
+}
+
char* SkArenaAlloc::SkipPod(char* footerEnd) {
char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
int32_t skip;
@@ -39,50 +97,6 @@
return nullptr;
}
-SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize)
- : fDtorCursor {block}
- , fCursor {block}
- , fEnd {block + SkTo<uint32_t>(size)}
- , fFirstBlock {block}
- , fFirstSize {SkTo<uint32_t>(size)}
- , fExtraSize {SkTo<uint32_t>(extraSize)}
-{
- if (size < sizeof(Footer)) {
- fEnd = fCursor = fDtorCursor = nullptr;
- }
-
- if (fCursor != nullptr) {
- this->installFooter(end_chain, 0);
- }
-}
-
-SkArenaAlloc::~SkArenaAlloc() {
- RunDtorsOnBlock(fDtorCursor);
-}
-
-void SkArenaAlloc::reset() {
- this->~SkArenaAlloc();
- new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize};
-}
-
-void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
- SkASSERT(padding < 64);
- int64_t actionInt = (int64_t)(intptr_t)action;
-
- // The top 14 bits should be either all 0s or all 1s. Check this.
- SkASSERT((actionInt << 6) >> 6 == actionInt);
- Footer encodedFooter = (actionInt << 6) | padding;
- memmove(fCursor, &encodedFooter, sizeof(Footer));
- fCursor += sizeof(Footer);
- fDtorCursor = fCursor;
-}
-
-void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
- memmove(fCursor, &ptr, sizeof(char*));
- fCursor += sizeof(char*);
- this->installFooter(action, padding);
-}
-
void SkArenaAlloc::installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding) {
memmove(fCursor, &value, sizeof(uint32_t));
fCursor += sizeof(uint32_t);
@@ -113,6 +127,11 @@
char* newBlock = new char[allocationSize];
+ if (fTotalSlop >= 0) {
+ fTotalAlloc += allocationSize;
+ fTotalSlop += fEnd - fCursor;
+ }
+
auto previousDtor = fDtorCursor;
fCursor = newBlock;
fDtorCursor = newBlock;
diff --git a/src/core/SkArenaAlloc.h b/src/core/SkArenaAlloc.h
index 488adfc..e838579 100644
--- a/src/core/SkArenaAlloc.h
+++ b/src/core/SkArenaAlloc.h
@@ -53,21 +53,30 @@
// For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There is an
// addition overhead when switching from POD data to non-POD data of typically 8 bytes.
//
+// You can track memory use by adding SkArenaAlloc::kTrack as the last parameter to any constructor.
+//
+// char storage[someNumber];
+// SkArenaAlloc alloc{storage, SkArenaAlloc::kTrack};
+//
+// This will print out a line for every destructor or reset call that has the total memory
+// allocated, the total slop (the unused portion of a block), and the slop of the last block.
+//
// If additional blocks are needed they are increased exponentially. This strategy bounds the
// recursion of the RunDtorsOnBlock to be limited to O(log size-of-memory). Block size grow using
// the Fibonacci sequence which means that for 2^32 memory there are 48 allocations, and for 2^48
// there are 71 allocations.
class SkArenaAlloc {
public:
- SkArenaAlloc(char* block, size_t size, size_t extraSize);
+ enum Tracking {kDontTrack, kTrack};
+ SkArenaAlloc(char* block, size_t size, size_t, Tracking tracking = kDontTrack);
template <size_t kSize>
- SkArenaAlloc(char (&block)[kSize], size_t extraSize = kSize)
- : SkArenaAlloc(block, kSize, extraSize)
+ SkArenaAlloc(char (&block)[kSize], size_t extraSize = kSize, Tracking tracking = kDontTrack)
+ : SkArenaAlloc(block, kSize, extraSize, tracking)
{}
- SkArenaAlloc(size_t extraSize)
- : SkArenaAlloc(nullptr, 0, extraSize)
+ SkArenaAlloc(size_t extraSize, Tracking tracking = kDontTrack)
+ : SkArenaAlloc(nullptr, 0, extraSize, tracking)
{}
~SkArenaAlloc();
@@ -197,6 +206,11 @@
char* const fFirstBlock;
const uint32_t fFirstSize;
const uint32_t fExtraSize;
+
+ // Track some useful stats. Track stats if fTotalSlop is >= 0;
+ uint32_t fTotalAlloc { 0};
+ int32_t fTotalSlop {-1};
+
// Use the Fibonacci sequence as the growth factor for block size. The size of the block
// allocated is fFib0 * fExtraSize. Using 2 ^ n * fExtraSize had too much slop for Android.
uint32_t fFib0 {1}, fFib1 {1};