summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAliaksey Kandratsenka <alk@tut.by>2013-11-09 17:35:24 -0800
committerAliaksey Kandratsenka <alk@tut.by>2014-02-22 12:37:54 -0800
commitc7ce50cd04ea08bd20d4ea4b2924e6a4451d2565 (patch)
treef5c41a2eb83bf0d5d43a018c2f97e86dce3d0ea6
parent1d707cd4a3dfe6f238a530f945291acfd5995042 (diff)
downloadgperftools-c7ce50cd04ea08bd20d4ea4b2924e6a4451d2565.tar.gz
issue-489: implemented API to set chromium-style de-committing
Chrome has code to decommit (release back to OS) every span that's released. I don't want to make it default, but indeed some applications may want to enable this mode. The code itself is taken from 2-way-merging of code from Chromium fork.
-rw-r--r--src/page_heap.cc58
-rw-r--r--src/page_heap.h9
-rw-r--r--src/tcmalloc.cc10
3 files changed, 72 insertions, 5 deletions
diff --git a/src/page_heap.cc b/src/page_heap.cc
index c6ecbb9..a60df4a 100644
--- a/src/page_heap.cc
+++ b/src/page_heap.cc
@@ -66,7 +66,8 @@ PageHeap::PageHeap()
pagemap_cache_(0),
scavenge_counter_(0),
// Start scavenging at kMaxPages list
- release_index_(kMaxPages) {
+ release_index_(kMaxPages),
+ aggressive_decommit_(false) {
COMPILE_ASSERT(kNumClasses <= (1 << PageMapCache::kValuebits), valuebits);
DLL_Init(&large_.normal);
DLL_Init(&large_.returned);
@@ -312,6 +313,13 @@ void PageHeap::Delete(Span* span) {
ASSERT(Check());
}
+bool PageHeap::MayMergeSpans(Span *span, Span *other) {
+ if (aggressive_decommit_) {
+ return other->location != Span::IN_USE;
+ }
+ return span->location == other->location;
+}
+
void PageHeap::MergeIntoFreeList(Span* span) {
ASSERT(span->location != Span::IN_USE);
@@ -320,16 +328,44 @@ void PageHeap::MergeIntoFreeList(Span* span) {
// entries for the pieces we are merging together because we only
// care about the pagemap entries for the boundaries.
//
- // Note that only similar spans are merged together. For example,
- // we do not coalesce "returned" spans with "normal" spans.
+ // Note: depending on aggressive_decommit_ mode we allow only
+ // similar spans to be coalesced.
+ //
+ // The following applies if aggressive_decommit_ is enabled:
+ //
+ // Note that the adjacent spans we merge into "span" may come out of a
+ // "normal" (committed) list, and cleanly merge with our IN_USE span, which
+ // is implicitly committed. If the adjacents spans are on the "returned"
+ // (decommitted) list, then we must get both spans into the same state before
+ // or after we coalesce them. The current code always decomits. This is
+ // achieved by blindly decommitting the entire coalesced region, which may
+ // include any combination of committed and decommitted spans, at the end of
+ // the method.
+
+ // TODO(jar): "Always decommit" causes some extra calls to commit when we are
+ // called in GrowHeap() during an allocation :-/. We need to eval the cost of
+ // that oscillation, and possibly do something to reduce it.
+
+ // TODO(jar): We need a better strategy for deciding to commit, or decommit,
+ // based on memory usage and free heap sizes.
+
+ uint64_t temp_committed = 0;
const PageID p = span->start;
const Length n = span->length;
Span* prev = GetDescriptor(p-1);
- if (prev != NULL && prev->location == span->location) {
+ if (prev != NULL && MayMergeSpans(span, prev)) {
// Merge preceding span into this span
ASSERT(prev->start + prev->length == p);
const Length len = prev->length;
+ if (aggressive_decommit_ && prev->location == Span::ON_RETURNED_FREELIST) {
+ // We're about to put the merge span into the returned freelist and call
+ // DecommitSpan() on it, which will mark the entire span including this
+ // one as released and decrease stats_.committed_bytes by the size of the
+ // merged span. To make the math work out we temporarily increase the
+ // stats_.committed_bytes amount.
+ temp_committed = prev->length << kPageShift;
+ }
RemoveFromFreeList(prev);
DeleteSpan(prev);
span->start -= len;
@@ -338,10 +374,14 @@ void PageHeap::MergeIntoFreeList(Span* span) {
Event(span, 'L', len);
}
Span* next = GetDescriptor(p+n);
- if (next != NULL && next->location == span->location) {
+ if (next != NULL && MayMergeSpans(span, next)) {
// Merge next span into this span
ASSERT(next->start == p+n);
const Length len = next->length;
+ if (aggressive_decommit_ && next->location == Span::ON_RETURNED_FREELIST) {
+ // See the comment below 'if (prev->location ...' for explanation.
+ temp_committed += next->length << kPageShift;
+ }
RemoveFromFreeList(next);
DeleteSpan(next);
span->length += len;
@@ -349,6 +389,14 @@ void PageHeap::MergeIntoFreeList(Span* span) {
Event(span, 'R', len);
}
+ if (aggressive_decommit_) {
+ if (DecommitSpan(span)) {
+ span->location = Span::ON_RETURNED_FREELIST;
+ stats_.committed_bytes += temp_committed;
+ } else {
+ ASSERT(temp_committed == 0);
+ }
+ }
PrependToFreeList(span);
}
diff --git a/src/page_heap.h b/src/page_heap.h
index a4f3d66..18abed1 100644
--- a/src/page_heap.h
+++ b/src/page_heap.h
@@ -192,6 +192,11 @@ class PERFTOOLS_DLL_DECL PageHeap {
}
void CacheSizeClass(PageID p, size_t cl) const { pagemap_cache_.Put(p, cl); }
+ bool GetAggressiveDecommit(void) {return aggressive_decommit_;}
+ void SetAggressiveDecommit(bool aggressive_decommit) {
+ aggressive_decommit_ = aggressive_decommit;
+ }
+
private:
// Allocates a big block of memory for the pagemap once we reach more than
// 128MB
@@ -291,11 +296,15 @@ class PERFTOOLS_DLL_DECL PageHeap {
// some unused spans.
bool EnsureLimit(Length n, bool allowRelease = true);
+ bool MayMergeSpans(Span *span, Span *other);
+
// Number of pages to deallocate before doing more scavenging
int64_t scavenge_counter_;
// Index of last free list where we released memory to the OS.
int release_index_;
+
+ bool aggressive_decommit_;
};
} // namespace tcmalloc
diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc
index 04ff2bb..530e4c5 100644
--- a/src/tcmalloc.cc
+++ b/src/tcmalloc.cc
@@ -700,6 +700,11 @@ class TCMallocImplementation : public MallocExtension {
return true;
}
+ if (strcmp(name, "tcmalloc.aggressive_memory_decommit") == 0) {
+ *value = size_t(Static::pageheap()->GetAggressiveDecommit());
+ return true;
+ }
+
return false;
}
@@ -712,6 +717,11 @@ class TCMallocImplementation : public MallocExtension {
return true;
}
+ if (strcmp(name, "tcmalloc.aggressive_memory_decommit") == 0) {
+ Static::pageheap()->SetAggressiveDecommit(value != 0);
+ return true;
+ }
+
return false;
}