Skip to content

Commit 7ed1fdb

Browse files
committed
Improve ArrayBuffer performance
Cherry-picked from Node.js: commit 31450fce7c8fe6804c84a7875d2f85a78b309125 Author: Fedor Indutny <[email protected]> Date: Mon Sep 7 17:40:43 2015 -0700 deps: improve ArrayBuffer performance in v8 This a backport of the following commits from the v8's upstream: * 1a8c38c * 206f12a * 9e3676d Original commit message: heap: make array buffer maps disjoint Remove intersection from the `std::map`s representing current live ArrayBuffers. While being simpler to understand, it poses significant performance issue for the active ArrayBuffer users (like node.js). Store buffers separately, and process them together during mark-sweep phase. The results of benchmarks are: $ ./node-slow bench && ./node-fast bench 4997.4 ns/op 4685.7 ns/op NOTE: `fast` - was a patched node.js, `slow` - unpatched node.js with vanilla v8. PR-URL: nodejs/node#2732 Reviewed-By: Rod Vagg <[email protected]> Reviewed-By: Brian White <[email protected]> Reviewed-By: Roman Reiss <[email protected]>
1 parent df50253 commit 7ed1fdb

File tree

3 files changed

+62
-101
lines changed

3 files changed

+62
-101
lines changed

src/heap/heap.cc

+59-86
Original file line numberDiff line numberDiff line change
@@ -1854,120 +1854,93 @@ void Heap::ProcessNativeContexts(WeakObjectRetainer* retainer) {
18541854
}
18551855

18561856

1857-
void Heap::RegisterNewArrayBufferHelper(std::map<void*, size_t>& live_buffers,
1858-
void* data, size_t length) {
1859-
live_buffers[data] = length;
1860-
}
1861-
1862-
1863-
void Heap::UnregisterArrayBufferHelper(
1864-
std::map<void*, size_t>& live_buffers,
1865-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data) {
1866-
DCHECK(live_buffers.count(data) > 0);
1867-
live_buffers.erase(data);
1868-
not_yet_discovered_buffers.erase(data);
1869-
}
1870-
1871-
1872-
void Heap::RegisterLiveArrayBufferHelper(
1873-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data) {
1874-
not_yet_discovered_buffers.erase(data);
1875-
}
1876-
1877-
1878-
size_t Heap::FreeDeadArrayBuffersHelper(
1879-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
1880-
std::map<void*, size_t>& not_yet_discovered_buffers) {
1881-
size_t freed_memory = 0;
1882-
for (auto buffer = not_yet_discovered_buffers.begin();
1883-
buffer != not_yet_discovered_buffers.end(); ++buffer) {
1884-
isolate->array_buffer_allocator()->Free(buffer->first, buffer->second);
1885-
freed_memory += buffer->second;
1886-
live_buffers.erase(buffer->first);
1887-
}
1888-
not_yet_discovered_buffers = live_buffers;
1889-
return freed_memory;
1890-
}
1891-
1892-
1893-
void Heap::TearDownArrayBuffersHelper(
1894-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
1895-
std::map<void*, size_t>& not_yet_discovered_buffers) {
1896-
for (auto buffer = live_buffers.begin(); buffer != live_buffers.end();
1897-
++buffer) {
1898-
isolate->array_buffer_allocator()->Free(buffer->first, buffer->second);
1899-
}
1900-
live_buffers.clear();
1901-
not_yet_discovered_buffers.clear();
1902-
}
1903-
1904-
19051857
void Heap::RegisterNewArrayBuffer(bool in_new_space, void* data,
19061858
size_t length) {
19071859
if (!data) return;
1908-
RegisterNewArrayBufferHelper(live_array_buffers_, data, length);
19091860
if (in_new_space) {
1910-
RegisterNewArrayBufferHelper(live_array_buffers_for_scavenge_, data,
1911-
length);
1861+
live_array_buffers_for_scavenge_[data] = length;
1862+
} else {
1863+
live_array_buffers_[data] = length;
19121864
}
1865+
1866+
// We may go over the limit of externally allocated memory here. We call the
1867+
// api function to trigger a GC in this case.
19131868
reinterpret_cast<v8::Isolate*>(isolate_)
19141869
->AdjustAmountOfExternalAllocatedMemory(length);
19151870
}
19161871

19171872

19181873
void Heap::UnregisterArrayBuffer(bool in_new_space, void* data) {
19191874
if (!data) return;
1920-
UnregisterArrayBufferHelper(live_array_buffers_,
1921-
not_yet_discovered_array_buffers_, data);
1922-
if (in_new_space) {
1923-
UnregisterArrayBufferHelper(live_array_buffers_for_scavenge_,
1924-
not_yet_discovered_array_buffers_for_scavenge_,
1925-
data);
1926-
}
1875+
1876+
std::map<void*, size_t>* live_buffers =
1877+
in_new_space ? &live_array_buffers_for_scavenge_ : &live_array_buffers_;
1878+
std::map<void*, size_t>* not_yet_discovered_buffers =
1879+
in_new_space ? &not_yet_discovered_array_buffers_for_scavenge_
1880+
: &not_yet_discovered_array_buffers_;
1881+
1882+
DCHECK(live_buffers->count(data) > 0);
1883+
live_buffers->erase(data);
1884+
not_yet_discovered_buffers->erase(data);
19271885
}
19281886

19291887

19301888
void Heap::RegisterLiveArrayBuffer(bool from_scavenge, void* data) {
19311889
// ArrayBuffer might be in the middle of being constructed.
19321890
if (data == undefined_value()) return;
1933-
RegisterLiveArrayBufferHelper(
1934-
from_scavenge ? not_yet_discovered_array_buffers_for_scavenge_
1935-
: not_yet_discovered_array_buffers_,
1936-
data);
1891+
if (from_scavenge) {
1892+
not_yet_discovered_array_buffers_for_scavenge_.erase(data);
1893+
} else if (!not_yet_discovered_array_buffers_.erase(data)) {
1894+
not_yet_discovered_array_buffers_for_scavenge_.erase(data);
1895+
}
19371896
}
19381897

19391898

19401899
void Heap::FreeDeadArrayBuffers(bool from_scavenge) {
1941-
if (from_scavenge) {
1942-
for (auto& buffer : not_yet_discovered_array_buffers_for_scavenge_) {
1943-
not_yet_discovered_array_buffers_.erase(buffer.first);
1944-
live_array_buffers_.erase(buffer.first);
1945-
}
1946-
} else {
1900+
size_t freed_memory = 0;
1901+
for (auto& buffer : not_yet_discovered_array_buffers_for_scavenge_) {
1902+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1903+
freed_memory += buffer.second;
1904+
live_array_buffers_for_scavenge_.erase(buffer.first);
1905+
}
1906+
1907+
if (!from_scavenge) {
19471908
for (auto& buffer : not_yet_discovered_array_buffers_) {
1948-
// Scavenge can't happend during evacuation, so we only need to update
1949-
// live_array_buffers_for_scavenge_.
1950-
// not_yet_discovered_array_buffers_for_scanvenge_ will be reset before
1951-
// the next scavenge run in PrepareArrayBufferDiscoveryInNewSpace.
1952-
live_array_buffers_for_scavenge_.erase(buffer.first);
1909+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1910+
freed_memory += buffer.second;
1911+
live_array_buffers_.erase(buffer.first);
19531912
}
19541913
}
1955-
size_t freed_memory = FreeDeadArrayBuffersHelper(
1956-
isolate_,
1957-
from_scavenge ? live_array_buffers_for_scavenge_ : live_array_buffers_,
1958-
from_scavenge ? not_yet_discovered_array_buffers_for_scavenge_
1959-
: not_yet_discovered_array_buffers_);
1960-
if (freed_memory) {
1961-
reinterpret_cast<v8::Isolate*>(isolate_)
1962-
->AdjustAmountOfExternalAllocatedMemory(
1963-
-static_cast<int64_t>(freed_memory));
1964-
}
1914+
1915+
not_yet_discovered_array_buffers_for_scavenge_ =
1916+
live_array_buffers_for_scavenge_;
1917+
if (!from_scavenge) not_yet_discovered_array_buffers_ = live_array_buffers_;
1918+
1919+
// Do not call through the api as this code is triggered while doing a GC.
1920+
amount_of_external_allocated_memory_ -= freed_memory;
19651921
}
19661922

19671923

19681924
void Heap::TearDownArrayBuffers() {
1969-
TearDownArrayBuffersHelper(isolate_, live_array_buffers_,
1970-
not_yet_discovered_array_buffers_);
1925+
size_t freed_memory = 0;
1926+
for (auto& buffer : live_array_buffers_) {
1927+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1928+
freed_memory += buffer.second;
1929+
}
1930+
for (auto& buffer : live_array_buffers_for_scavenge_) {
1931+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1932+
freed_memory += buffer.second;
1933+
}
1934+
live_array_buffers_.clear();
1935+
live_array_buffers_for_scavenge_.clear();
1936+
not_yet_discovered_array_buffers_.clear();
1937+
not_yet_discovered_array_buffers_for_scavenge_.clear();
1938+
1939+
if (freed_memory > 0) {
1940+
reinterpret_cast<v8::Isolate*>(isolate_)
1941+
->AdjustAmountOfExternalAllocatedMemory(
1942+
-static_cast<int64_t>(freed_memory));
1943+
}
19711944
}
19721945

19731946

@@ -1985,7 +1958,7 @@ void Heap::PromoteArrayBuffer(Object* obj) {
19851958
// ArrayBuffer might be in the middle of being constructed.
19861959
if (data == undefined_value()) return;
19871960
DCHECK(live_array_buffers_for_scavenge_.count(data) > 0);
1988-
DCHECK(live_array_buffers_.count(data) > 0);
1961+
live_array_buffers_[data] = live_array_buffers_for_scavenge_[data];
19891962
live_array_buffers_for_scavenge_.erase(data);
19901963
not_yet_discovered_array_buffers_for_scavenge_.erase(data);
19911964
}

src/heap/heap.h

-15
Original file line numberDiff line numberDiff line change
@@ -2147,21 +2147,6 @@ class Heap {
21472147
// Called on heap tear-down. Frees all remaining ArrayBuffer backing stores.
21482148
void TearDownArrayBuffers();
21492149

2150-
// These correspond to the non-Helper versions.
2151-
void RegisterNewArrayBufferHelper(std::map<void*, size_t>& live_buffers,
2152-
void* data, size_t length);
2153-
void UnregisterArrayBufferHelper(
2154-
std::map<void*, size_t>& live_buffers,
2155-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data);
2156-
void RegisterLiveArrayBufferHelper(
2157-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data);
2158-
size_t FreeDeadArrayBuffersHelper(
2159-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
2160-
std::map<void*, size_t>& not_yet_discovered_buffers);
2161-
void TearDownArrayBuffersHelper(
2162-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
2163-
std::map<void*, size_t>& not_yet_discovered_buffers);
2164-
21652150
// Record statistics before and after garbage collection.
21662151
void ReportStatisticsBeforeGC();
21672152
void ReportStatisticsAfterGC();

src/heap/mark-compact.cc

+3
Original file line numberDiff line numberDiff line change
@@ -4310,6 +4310,9 @@ void MarkCompactCollector::SweepSpaces() {
43104310

43114311
EvacuateNewSpaceAndCandidates();
43124312

4313+
// NOTE: ArrayBuffers must be evacuated first, before freeing them. Otherwise
4314+
// not yet discovered buffers for scavenge will have all of them, and they
4315+
// will be erroneously freed.
43134316
heap()->FreeDeadArrayBuffers(false);
43144317

43154318
// ClearNonLiveReferences depends on precise sweeping of map space to

0 commit comments

Comments
 (0)