Skip to content

Commit 1fdec65

Browse files
indutnyofrobots
authored andcommitted
deps: improve ArrayBuffer performance in v8
This a backport of the following commits from the v8's upstream: * 1a8c38c50513f9af07ada479629a653e1cf36ff3 * 206f12abee3f1e7eda8fc6521d48f3c319460ee1 * 9e3676da9ab1aaf7de3e8582cb3fdefcc3dbaf33 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. Ref: #2732 PR-URL: #3351 Reviewed-By: indutny - Fedor Indutny <[email protected]> Reviewed-By: bnoordhuis - Ben Noordhuis <[email protected]>
1 parent 5cd1fd8 commit 1fdec65

File tree

3 files changed

+57
-96
lines changed

3 files changed

+57
-96
lines changed

deps/v8/src/heap/heap.cc

+54-81
Original file line numberDiff line numberDiff line change
@@ -1791,61 +1791,13 @@ void Heap::ProcessNativeContexts(WeakObjectRetainer* retainer) {
17911791
}
17921792

17931793

1794-
void Heap::RegisterNewArrayBufferHelper(std::map<void*, size_t>& live_buffers,
1795-
void* data, size_t length) {
1796-
live_buffers[data] = length;
1797-
}
1798-
1799-
1800-
void Heap::UnregisterArrayBufferHelper(
1801-
std::map<void*, size_t>& live_buffers,
1802-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data) {
1803-
DCHECK(live_buffers.count(data) > 0);
1804-
live_buffers.erase(data);
1805-
not_yet_discovered_buffers.erase(data);
1806-
}
1807-
1808-
1809-
void Heap::RegisterLiveArrayBufferHelper(
1810-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data) {
1811-
not_yet_discovered_buffers.erase(data);
1812-
}
1813-
1814-
1815-
size_t Heap::FreeDeadArrayBuffersHelper(
1816-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
1817-
std::map<void*, size_t>& not_yet_discovered_buffers) {
1818-
size_t freed_memory = 0;
1819-
for (auto buffer = not_yet_discovered_buffers.begin();
1820-
buffer != not_yet_discovered_buffers.end(); ++buffer) {
1821-
isolate->array_buffer_allocator()->Free(buffer->first, buffer->second);
1822-
freed_memory += buffer->second;
1823-
live_buffers.erase(buffer->first);
1824-
}
1825-
not_yet_discovered_buffers = live_buffers;
1826-
return freed_memory;
1827-
}
1828-
1829-
1830-
void Heap::TearDownArrayBuffersHelper(
1831-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
1832-
std::map<void*, size_t>& not_yet_discovered_buffers) {
1833-
for (auto buffer = live_buffers.begin(); buffer != live_buffers.end();
1834-
++buffer) {
1835-
isolate->array_buffer_allocator()->Free(buffer->first, buffer->second);
1836-
}
1837-
live_buffers.clear();
1838-
not_yet_discovered_buffers.clear();
1839-
}
1840-
1841-
18421794
void Heap::RegisterNewArrayBuffer(bool in_new_space, void* data,
18431795
size_t length) {
18441796
if (!data) return;
1845-
RegisterNewArrayBufferHelper(live_array_buffers_, data, length);
18461797
if (in_new_space) {
1847-
RegisterNewArrayBufferHelper(live_array_buffers_for_scavenge_, data,
1848-
length);
1798+
live_array_buffers_for_scavenge_[data] = length;
1799+
} else {
1800+
live_array_buffers_[data] = length;
18491801
}
18501802

18511803
// We may go over the limit of externally allocated memory here. We call the
@@ -1857,54 +1809,75 @@ void Heap::RegisterNewArrayBuffer(bool in_new_space, void* data,
18571809

18581810
void Heap::UnregisterArrayBuffer(bool in_new_space, void* data) {
18591811
if (!data) return;
1860-
UnregisterArrayBufferHelper(live_array_buffers_,
1861-
not_yet_discovered_array_buffers_, data);
1862-
if (in_new_space) {
1863-
UnregisterArrayBufferHelper(live_array_buffers_for_scavenge_,
1864-
not_yet_discovered_array_buffers_for_scavenge_,
1865-
data);
1866-
}
1812+
1813+
std::map<void*, size_t>* live_buffers =
1814+
in_new_space ? &live_array_buffers_for_scavenge_ : &live_array_buffers_;
1815+
std::map<void*, size_t>* not_yet_discovered_buffers =
1816+
in_new_space ? &not_yet_discovered_array_buffers_for_scavenge_
1817+
: &not_yet_discovered_array_buffers_;
1818+
1819+
DCHECK(live_buffers->count(data) > 0);
1820+
live_buffers->erase(data);
1821+
not_yet_discovered_buffers->erase(data);
18671822
}
18681823

18691824

18701825
void Heap::RegisterLiveArrayBuffer(bool from_scavenge, void* data) {
18711826
// ArrayBuffer might be in the middle of being constructed.
18721827
if (data == undefined_value()) return;
1873-
RegisterLiveArrayBufferHelper(
1874-
from_scavenge ? not_yet_discovered_array_buffers_for_scavenge_
1875-
: not_yet_discovered_array_buffers_,
1876-
data);
1828+
if (from_scavenge) {
1829+
not_yet_discovered_array_buffers_for_scavenge_.erase(data);
1830+
} else if (!not_yet_discovered_array_buffers_.erase(data)) {
1831+
not_yet_discovered_array_buffers_for_scavenge_.erase(data);
1832+
}
18771833
}
18781834

18791835

18801836
void Heap::FreeDeadArrayBuffers(bool from_scavenge) {
1881-
if (from_scavenge) {
1882-
for (auto& buffer : not_yet_discovered_array_buffers_for_scavenge_) {
1883-
not_yet_discovered_array_buffers_.erase(buffer.first);
1884-
live_array_buffers_.erase(buffer.first);
1885-
}
1886-
} else {
1837+
size_t freed_memory = 0;
1838+
for (auto& buffer : not_yet_discovered_array_buffers_for_scavenge_) {
1839+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1840+
freed_memory += buffer.second;
1841+
live_array_buffers_for_scavenge_.erase(buffer.first);
1842+
}
1843+
1844+
if (!from_scavenge) {
18871845
for (auto& buffer : not_yet_discovered_array_buffers_) {
1888-
// Scavenge can't happend during evacuation, so we only need to update
1889-
// live_array_buffers_for_scavenge_.
1890-
// not_yet_discovered_array_buffers_for_scanvenge_ will be reset before
1891-
// the next scavenge run in PrepareArrayBufferDiscoveryInNewSpace.
1892-
live_array_buffers_for_scavenge_.erase(buffer.first);
1846+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1847+
freed_memory += buffer.second;
1848+
live_array_buffers_.erase(buffer.first);
18931849
}
18941850
}
18951851

1852+
not_yet_discovered_array_buffers_for_scavenge_ =
1853+
live_array_buffers_for_scavenge_;
1854+
if (!from_scavenge) not_yet_discovered_array_buffers_ = live_array_buffers_;
1855+
18961856
// Do not call through the api as this code is triggered while doing a GC.
1897-
amount_of_external_allocated_memory_ -= FreeDeadArrayBuffersHelper(
1898-
isolate_,
1899-
from_scavenge ? live_array_buffers_for_scavenge_ : live_array_buffers_,
1900-
from_scavenge ? not_yet_discovered_array_buffers_for_scavenge_
1901-
: not_yet_discovered_array_buffers_);
1857+
amount_of_external_allocated_memory_ -= freed_memory;
19021858
}
19031859

19041860

19051861
void Heap::TearDownArrayBuffers() {
1906-
TearDownArrayBuffersHelper(isolate_, live_array_buffers_,
1907-
not_yet_discovered_array_buffers_);
1862+
size_t freed_memory = 0;
1863+
for (auto& buffer : live_array_buffers_) {
1864+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1865+
freed_memory += buffer.second;
1866+
}
1867+
for (auto& buffer : live_array_buffers_for_scavenge_) {
1868+
isolate()->array_buffer_allocator()->Free(buffer.first, buffer.second);
1869+
freed_memory += buffer.second;
1870+
}
1871+
live_array_buffers_.clear();
1872+
live_array_buffers_for_scavenge_.clear();
1873+
not_yet_discovered_array_buffers_.clear();
1874+
not_yet_discovered_array_buffers_for_scavenge_.clear();
1875+
1876+
if (freed_memory > 0) {
1877+
reinterpret_cast<v8::Isolate*>(isolate_)
1878+
->AdjustAmountOfExternalAllocatedMemory(
1879+
-static_cast<int64_t>(freed_memory));
1880+
}
19081881
}
19091882

19101883

@@ -1922,7 +1895,7 @@ void Heap::PromoteArrayBuffer(Object* obj) {
19221895
// ArrayBuffer might be in the middle of being constructed.
19231896
if (data == undefined_value()) return;
19241897
DCHECK(live_array_buffers_for_scavenge_.count(data) > 0);
1925-
DCHECK(live_array_buffers_.count(data) > 0);
1898+
live_array_buffers_[data] = live_array_buffers_for_scavenge_[data];
19261899
live_array_buffers_for_scavenge_.erase(data);
19271900
not_yet_discovered_array_buffers_for_scavenge_.erase(data);
19281901
}

deps/v8/src/heap/heap.h

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

2098-
// These correspond to the non-Helper versions.
2099-
void RegisterNewArrayBufferHelper(std::map<void*, size_t>& live_buffers,
2100-
void* data, size_t length);
2101-
void UnregisterArrayBufferHelper(
2102-
std::map<void*, size_t>& live_buffers,
2103-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data);
2104-
void RegisterLiveArrayBufferHelper(
2105-
std::map<void*, size_t>& not_yet_discovered_buffers, void* data);
2106-
size_t FreeDeadArrayBuffersHelper(
2107-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
2108-
std::map<void*, size_t>& not_yet_discovered_buffers);
2109-
void TearDownArrayBuffersHelper(
2110-
Isolate* isolate, std::map<void*, size_t>& live_buffers,
2111-
std::map<void*, size_t>& not_yet_discovered_buffers);
2112-
21132098
// Record statistics before and after garbage collection.
21142099
void ReportStatisticsBeforeGC();
21152100
void ReportStatisticsAfterGC();

deps/v8/src/heap/mark-compact.cc

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

43584358
EvacuateNewSpaceAndCandidates();
43594359

4360+
// NOTE: ArrayBuffers must be evacuated first, before freeing them. Otherwise
4361+
// not yet discovered buffers for scavenge will have all of them, and they
4362+
// will be erroneously freed.
43604363
heap()->FreeDeadArrayBuffers(false);
43614364

43624365
// Deallocate unmarked objects and clear marked bits for marked objects.

0 commit comments

Comments
 (0)