New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lib: reduce memory allocation for arrays #44474
Conversation
I like the idea. Do you have a benchmark and a memory profile where you compare both situations in a heavy load scenario? It would be good to see that it does indeed reduce the memory amount. This is always tricky as V8 might already do a great job detecting the old arrays and to keep the memory low. |
I do not know how to benchmark. How can I benchmark? |
This comment was marked as resolved.
This comment was marked as resolved.
I got good benchmark scores from two new versions (However, need more confidence). @BridgeAR Which is better? Non-reducible buffer version
class FixedCircularBuffer {
constructor() {
this.bottom = 0;
this.top = 0;
this.list = new Array(kSize);
this.next = null;
}
isEmpty() {
return this.top === this.bottom;
}
isFull() {
return ((this.top + 1) & kMask) === this.bottom;
}
push(data) {
this.list[this.top] = data;
this.top = (this.top + 1) & kMask;
}
shift() {
if (this.isEmpty())
return null;
const data = this.list[this.bottom];
this.list[this.bottom] = undefined;
this.bottom = (this.bottom + 1) & kMask;
return data;
}
}
module.exports = class FixedQueue {
constructor() {
this.head = this.tail = this.buffer = new FixedCircularBuffer();
}
isEmpty() {
return this.head.isEmpty();
}
push(data) {
const head = this.head;
if (head.isFull()) {
// Head is full: Reuses the empty tail or creates a new queue,
// sets the old queue's `.next` to it, and sets it as the new main queue.
const buffer = this.buffer;
if (buffer.isEmpty()) {
this.buffer = buffer.next;
buffer.next = null;
this.head = head.next = buffer;
} else {
this.head = head.next = new FixedCircularBuffer();
}
}
this.head.push(data);
}
shift() {
const tail = this.tail;
const data = tail.shift();
if (tail.isEmpty() && tail.next !== null) {
// If the next is not empty, it forms the new tail.
this.tail = tail.next;
}
return data;
}
}; Reducible buffer version
class FixedCircularBuffer {
constructor() {
this.bottom = 0;
this.top = 0;
this.list = new Array(kSize);
this.next = null;
}
isEmpty() {
return this.top === this.bottom;
}
isFull() {
return ((this.top + 1) & kMask) === this.bottom;
}
push(data) {
this.list[this.top] = data;
this.top = (this.top + 1) & kMask;
}
shift() {
if (this.isEmpty())
return null;
const data = this.list[this.bottom];
this.list[this.bottom] = undefined;
this.bottom = (this.bottom + 1) & kMask;
return data;
}
}
module.exports = class FixedQueue {
constructor() {
this.head = this.tail = this.buffer = new FixedCircularBuffer();
}
isEmpty() {
return this.head.isEmpty();
}
push(data) {
const head = this.head;
if (head.isFull()) {
// Head is full: Reuses the empty tail or creates a new queue,
// sets the old queue's `.next` to it, and sets it as the new main queue.
let buffer = this.buffer;
if (buffer.isEmpty()) {
if (buffer.next?.isEmpty()) {
buffer = buffer.next;
}
this.buffer = buffer.next;
buffer.next = null;
this.head = head.next = buffer;
} else {
this.head = head.next = new FixedCircularBuffer();
}
}
this.head.push(data);
}
shift() {
const tail = this.tail;
const data = tail.shift();
if (tail.isEmpty() && tail.next !== null) {
// If the next is not empty, it forms the new tail.
this.tail = tail.next;
}
return data;
}
}; |
Move to #44499 |
Probably, management cost of an averagely longer linked list is higher than creating new arrays even if just replacing the contained value of a substantially fixed-length circular linked list. |
The current implementation disposes used empty arrays and creates a new array. This PR reuses used empty arrays instead of creating a new array. This patch would reduce memory allocation time for
Array(2048)
.