bumparena 0.0.7 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -17
- package/dist/arena.d.ts +64 -0
- package/interface.d.ts +48 -0
- package/package.json +1 -1
- package/bench/bench.md +0 -38
package/README.md
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
# BumpArena
|
|
1
|
+
# 🚀 BumpArena: High-Performance Memory Management for Bun/TS
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/bumparena)
|
|
4
|
+
[](https://github.com/dein-nutzername/bumparena/blob/main/LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**4.2x faster than standard arrays while using ~60% less RAM!** 💾⚡
|
|
7
|
+
|
|
8
|
+
BumpArena is a high-performance memory arena designed for JavaScript and TypeScript (optimized for Bun). It provides contiguous memory allocation, fast pointer-based access, and minimal Garbage Collection (GC) overhead, making it the ideal choice for handling industrial-scale datasets and real-time telemetry.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 📊 Performance Benchmark (50M Objects)
|
|
13
|
+
|
|
14
|
+
I compared the **BumpArena** implementation against a standard **Array-based** approach by processing a dataset of 50 million records.
|
|
15
|
+
|
|
16
|
+
| Metric | Standard Array | **BumpArena** | Improvement |
|
|
17
|
+
| :--- | :--- | :--- | :--- |
|
|
18
|
+
| **Total Time** | 765.9s (12.7 min) | **183.1s (3.05 min)** | **4.2x Faster** 🚀 |
|
|
19
|
+
| **Throughput** | ~652k lines/s | **~2.73M lines/s** | **+318% Speed** |
|
|
20
|
+
| **RAM Usage (RSS)** | 28.04 GB | **11.24 GB** | **16.8 GB Saved** 📉 |
|
|
21
|
+
| **Heap Efficiency** | 11.22 GB | **4.82 GB** | **2.3x Lower Usage** |
|
|
22
|
+
|
|
23
|
+
### 🛠 Why it's faster:
|
|
24
|
+
* **Zero GC Pressure:** By using a pre-allocated buffer, we bypass the costly JavaScript Garbage Collector.
|
|
25
|
+
* **Cache Locality:** Contiguous memory layout ensures the CPU stays fast and avoids cache misses.
|
|
26
|
+
* **Industrial Scale:** Designed to handle 50M+ data points without breaking the heap.
|
|
6
27
|
|
|
7
28
|
---
|
|
8
29
|
|
|
@@ -34,25 +55,30 @@ yarn add bumparena
|
|
|
34
55
|
|
|
35
56
|
```ts
|
|
36
57
|
import { Arena } from "bumparena";
|
|
58
|
+
import fs from "node:fs";
|
|
59
|
+
|
|
60
|
+
// 1. Initialize (e.g., 1GB Arena)
|
|
61
|
+
const arena = new Arena({ initalSize: 1024 * 1024 * 1024 });
|
|
37
62
|
|
|
38
|
-
//
|
|
39
|
-
const
|
|
63
|
+
// 2. Map data instantly (Zero-Copy)
|
|
64
|
+
const data = new Uint8Array([10, 20, 30, 40]);
|
|
65
|
+
const ptr = arena.alloc(data);
|
|
40
66
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
const ptr = arena.alloc(data);
|
|
67
|
+
// 3. Retrieve (O(1) access)
|
|
68
|
+
const view = arena.read(ptr);
|
|
44
69
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
70
|
+
// 4. Persistence "From Nothing"
|
|
71
|
+
// Save the entire memory state 1:1 as a binary image
|
|
72
|
+
fs.writeFileSync("database.bin", arena.getBuffer());
|
|
48
73
|
|
|
49
|
-
//
|
|
50
|
-
|
|
74
|
+
// 5. Reload (Zero Parsing Time)
|
|
75
|
+
// Simply load the bytes back into a new Arena buffer
|
|
76
|
+
const savedData = fs.readFileSync("database.bin");
|
|
77
|
+
const restoredArena = new Arena({ initalSize: savedData.byteLength });
|
|
78
|
+
restoredArena.putBytes(savedData); // Structure is restored instantly
|
|
51
79
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
reserved.set(new Uint8Array([10, 20, 30]));
|
|
55
|
-
console.log(reserved);
|
|
80
|
+
//Clear your Arena, if you want a restart
|
|
81
|
+
arena.clear()
|
|
56
82
|
```
|
|
57
83
|
|
|
58
84
|
---
|
|
@@ -66,6 +92,99 @@ console.log(reserved);
|
|
|
66
92
|
|
|
67
93
|
---
|
|
68
94
|
|
|
95
|
+
## Binary Header Specification
|
|
96
|
+
|
|
97
|
+
BumpArena uses a fixed 16-byte packed header. Alignment is applied to the entire block, ensuring that every new Header starts at a memory address divisible by your chosen alignment (e.g., 8, 16, 32 bytes).
|
|
98
|
+
|
|
99
|
+
### Memory Layout & Alignment
|
|
100
|
+
|
|
101
|
+
The total_length field does not just represent the sum of the header and data; it includes the padding required to align the next block in the Arena.
|
|
102
|
+
|
|
103
|
+
| Offset (Byte) | Field | Type | Description |
|
|
104
|
+
| :--- | :--- | :--- | :--- |
|
|
105
|
+
| `0x00` | `total_length` | `uint32` | Header + Payload + Alignment Padding |
|
|
106
|
+
| `0x04` | `payload_length` | `uint32` | Exact size of the user data |
|
|
107
|
+
| `0x08` | `generation` | `uint32` | Validation counter (prevents ABA/stale pointer issues) |
|
|
108
|
+
| `0x0C` | `deleted` | `uint8` | Status flag (`0x01` = deleted, `0x00` = active) |
|
|
109
|
+
| `0x0D` | `user_header0` | `uint8` | Custom metadata slot 1 |
|
|
110
|
+
| `0x0E` | `user_header1` | `uint8` | Custom metadata slot 2 |
|
|
111
|
+
| `0x0F` | `user_header2` | `uint8` | Custom metadata slot 3 |
|
|
112
|
+
| `0x10` | **Payload** | `u8[]` | User data starts here (Fixed Offset) |
|
|
113
|
+
| `...` | **Padding** | `u8[N]` | Internal padding to align the next Block |
|
|
114
|
+
|
|
115
|
+
### Implementation Examples
|
|
116
|
+
|
|
117
|
+
Typescript
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// Example: How BumpArena maps the header internally
|
|
121
|
+
const view = new DataView(arena.getBuffer().buffer);
|
|
122
|
+
const ptr = 0x1234; // Current allocation pointer
|
|
123
|
+
|
|
124
|
+
const header = {
|
|
125
|
+
totalLength: view.getUint32(ptr + 0, true), // Little-endian
|
|
126
|
+
payloadLength: view.getUint32(ptr + 4, true),
|
|
127
|
+
generation: view.getUint32(ptr + 8, true),
|
|
128
|
+
deleted: view.getUint8(ptr + 12),
|
|
129
|
+
userHeader: new Uint8Array(arena.getBuffer().buffer, ptr + 13, 3)
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// The actual payload starts at ptr + 16
|
|
133
|
+
const payload = new Uint8Array(arena.getBuffer().buffer, ptr + 16, header.payloadLength);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
C/++
|
|
137
|
+
|
|
138
|
+
```c
|
|
139
|
+
typedef struct __attribute__((packed)) {
|
|
140
|
+
uint32_t total_length; // Jump to next header: current_ptr + total_length
|
|
141
|
+
uint32_t payload_length;
|
|
142
|
+
uint32_t generation;
|
|
143
|
+
uint8_t deleted;
|
|
144
|
+
uint8_t user_header0;
|
|
145
|
+
uint8_t user_header1;
|
|
146
|
+
uint8_t user_header2;uint8_t payload[]; // Data starts at offset 16 (0x10)
|
|
147
|
+
} ArenaData;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Rust
|
|
151
|
+
|
|
152
|
+
```rust
|
|
153
|
+
#[repr(C, packed)]
|
|
154
|
+
pub struct ArenaData {
|
|
155
|
+
pub total_length: u32, // Jump to next header: current_ptr + total_length
|
|
156
|
+
pub payload_length: u32,
|
|
157
|
+
pub generation: u32,
|
|
158
|
+
pub deleted: u8,
|
|
159
|
+
pub user_header0: u8,
|
|
160
|
+
pub user_header1: u8,
|
|
161
|
+
pub user_header2: u8,
|
|
162
|
+
// Payload follows immediately at offset 16
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
go
|
|
167
|
+
|
|
168
|
+
```go
|
|
169
|
+
type ArenaData struct {
|
|
170
|
+
TotalLength uint32 // Jump to next header: current_ptr + total_length
|
|
171
|
+
PayloadLength uint32 // the length of the Payload itself
|
|
172
|
+
Generation uint32 // The Generation bits
|
|
173
|
+
Deleted uint8 // 0x1=true,0x0=false
|
|
174
|
+
UserHeader [3]uint8 // User Metadata
|
|
175
|
+
// Payload follows immediately at offset 16
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Key Advantages
|
|
180
|
+
Zero-Copy: Directly cast your binary data to these structures in any language.
|
|
181
|
+
|
|
182
|
+
Stale Pointer Protection: The generation field allows you to verify if a pointer still refers to the original data or if the memory has been reused.
|
|
183
|
+
|
|
184
|
+
Alignment: 16-byte boundaries are a "sweet spot" for modern hardware architectures,.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
69
188
|
## Benchmarks
|
|
70
189
|
|
|
71
190
|
| Implementation | Time | Heap Used | Notes |
|
package/dist/arena.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type ArenaLocation = bigint & {
|
|
2
|
+
readonly __data_pointer: unique symbol;
|
|
3
|
+
};
|
|
4
|
+
export interface ArenaOptions {
|
|
5
|
+
initalSize?: number;
|
|
6
|
+
littleEndian?: boolean;
|
|
7
|
+
allignment?: 8 | 16 | 32 | 64;
|
|
8
|
+
bucketOffsets?: number[];
|
|
9
|
+
bucketCapacities?: number[];
|
|
10
|
+
}
|
|
11
|
+
export interface ArenaCustomHeaders {
|
|
12
|
+
header0: number;
|
|
13
|
+
header1: number;
|
|
14
|
+
header2: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ArenaHeaders {
|
|
17
|
+
totalLength: number;
|
|
18
|
+
payloadlength: number;
|
|
19
|
+
deleted: boolean;
|
|
20
|
+
header0: number;
|
|
21
|
+
header1: number;
|
|
22
|
+
header2: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class Arena {
|
|
25
|
+
private HEADER_SIZE_BYTES;
|
|
26
|
+
private _buffer;
|
|
27
|
+
private _view8;
|
|
28
|
+
private _view32;
|
|
29
|
+
private _offset;
|
|
30
|
+
private _emptySpots;
|
|
31
|
+
private _allignMask;
|
|
32
|
+
private _allignShift;
|
|
33
|
+
private _bucketOffsets;
|
|
34
|
+
private _bucketCapacities;
|
|
35
|
+
private _bucketcount;
|
|
36
|
+
constructor(options?: ArenaOptions);
|
|
37
|
+
private _u;
|
|
38
|
+
private _idx32;
|
|
39
|
+
private _makePtr;
|
|
40
|
+
private _getOffset;
|
|
41
|
+
private _getBucketCount;
|
|
42
|
+
private _setBucketCount;
|
|
43
|
+
private _getBucketOffset;
|
|
44
|
+
private _setBucketOffset;
|
|
45
|
+
private _initBlock;
|
|
46
|
+
alloc(data: Uint8Array, headers?: ArenaCustomHeaders): ArenaLocation;
|
|
47
|
+
read(location: ArenaLocation): Uint8Array | null;
|
|
48
|
+
free(location: ArenaLocation): ArenaLocation;
|
|
49
|
+
private _checkForSpace;
|
|
50
|
+
private _resize;
|
|
51
|
+
size(): number;
|
|
52
|
+
getBuffer(): Uint8Array;
|
|
53
|
+
reserve(size: number): Uint8Array;
|
|
54
|
+
translate(ptr: ArenaLocation): {
|
|
55
|
+
start: number;
|
|
56
|
+
generation: bigint;
|
|
57
|
+
};
|
|
58
|
+
readWithHeaders(ptr: ArenaLocation): Uint8Array | null;
|
|
59
|
+
label(): Array<ArenaLocation>;
|
|
60
|
+
getHeaders(ptr: ArenaLocation): ArenaHeaders;
|
|
61
|
+
estimate(size: number, amnt: number): number;
|
|
62
|
+
directAlloc(source: Uint8Array, startn: number, endn: number): ArenaLocation;
|
|
63
|
+
clear(): void;
|
|
64
|
+
}
|
package/interface.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type ArenaLocation = bigint & { readonly __data_pointer: unique symbol };
|
|
2
|
+
export interface ArenaOptions {
|
|
3
|
+
initalSize?: number
|
|
4
|
+
littleEndian?: boolean
|
|
5
|
+
allignment?: 8 | 16 | 32 | 64
|
|
6
|
+
bucketOffsets?: number[];
|
|
7
|
+
bucketCapacities?: number[];
|
|
8
|
+
}
|
|
9
|
+
export interface ArenaCustomHeaders {
|
|
10
|
+
header0: number,
|
|
11
|
+
header1: number,
|
|
12
|
+
header2: number
|
|
13
|
+
}
|
|
14
|
+
export interface InspectStruct {
|
|
15
|
+
offset: number;
|
|
16
|
+
generation_ptr: number;
|
|
17
|
+
generation: number;
|
|
18
|
+
isSafe: boolean;
|
|
19
|
+
totalLength: number;
|
|
20
|
+
payloadLength: number;
|
|
21
|
+
isDeleted: boolean;
|
|
22
|
+
UserMetaData0: number;
|
|
23
|
+
UserMetaData1: number;
|
|
24
|
+
UserMetaData2: number;
|
|
25
|
+
payload?: Uint8Array;
|
|
26
|
+
}
|
|
27
|
+
export interface ArenaHeaders {
|
|
28
|
+
totalLength: number
|
|
29
|
+
payloadLength: number
|
|
30
|
+
deleted: boolean
|
|
31
|
+
header0: number
|
|
32
|
+
header1: number
|
|
33
|
+
header2: number
|
|
34
|
+
}
|
|
35
|
+
export interface IStorageStrategy {
|
|
36
|
+
alloc(data: Uint8Array, headers?: ArenaCustomHeaders): ArenaLocation
|
|
37
|
+
read(location: ArenaLocation): Uint8Array | null
|
|
38
|
+
free(location: ArenaLocation): ArenaLocation
|
|
39
|
+
estimate(size: number, amnt: number): number
|
|
40
|
+
reset(): void
|
|
41
|
+
clear(): void
|
|
42
|
+
|
|
43
|
+
/** Eager Collection */
|
|
44
|
+
collectActiveRecords(): ArenaLocation[]
|
|
45
|
+
|
|
46
|
+
/** Lazy Iterator */
|
|
47
|
+
records(): Generator<[Uint8Array, ArenaLocation]>
|
|
48
|
+
}
|
package/package.json
CHANGED
package/bench/bench.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Benchmark 1
|
|
2
|
-
|
|
3
|
-
## bun arena.ts;
|
|
4
|
-
183098.55 ms
|
|
5
|
-
{
|
|
6
|
-
rss: 11240919040,
|
|
7
|
-
heapTotal: 8595075072,
|
|
8
|
-
heapUsed: 4817152633,
|
|
9
|
-
external: 2157456969,
|
|
10
|
-
arrayBuffers: 2156686216,
|
|
11
|
-
}
|
|
12
|
-
arenasize: 2097152 KB
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## bun array.ts
|
|
17
|
-
765961.27 ms
|
|
18
|
-
Count: 50000000
|
|
19
|
-
{
|
|
20
|
-
rss: 28036014080,
|
|
21
|
-
heapTotal: 9476152320,
|
|
22
|
-
heapUsed: 11222438346,
|
|
23
|
-
external: 7170077276,
|
|
24
|
-
arrayBuffers: 2921230601,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Direct Comparison
|
|
30
|
-
|
|
31
|
-
| Metric | Arena Implementation (Optimized) | Standard Array Implementation | Difference / Factor |
|
|
32
|
-
| :--- | :--- | :--- | :--- |
|
|
33
|
-
| **Total Time** | **183,098 ms** (3.05 min) | **765,961 ms** (12.76 min) | **~4.2x Faster** |
|
|
34
|
-
| **Time per 50M Lines** | **~18.3 sec** | **~76.6 sec** | **- 58.3 sec / Round** |
|
|
35
|
-
| **RAM Usage (RSS)** | **11.24 GB** | **28.04 GB** | **16.8 GB Saved** |
|
|
36
|
-
| **Heap Used (JS Objects)** | **4.82 GB** | **11.22 GB** | **2.3x More Efficient** |
|
|
37
|
-
| **External (Buffer)** | **2.16 GB** | **7.17 GB** | **Compact Memory Footprint** |
|
|
38
|
-
|