@calcit/ternary-tree 0.0.24 → 0.0.26
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/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +6 -0
- package/README.md +24 -4
- package/TESTING.md +155 -0
- package/lib/bench.d.mts +11 -0
- package/lib/bench.mjs +99 -0
- package/lib/benchmark.d.mts +1 -0
- package/lib/benchmark.mjs +67 -0
- package/lib/list.d.mts +1 -0
- package/lib/list.mjs +265 -188
- package/lib/map.mjs +223 -161
- package/lib/test-list-detailed-perf.d.mts +1 -0
- package/lib/test-list-detailed-perf.mjs +165 -0
- package/lib/test-list-perf.d.mts +1 -0
- package/lib/test-list-perf.mjs +153 -0
- package/lib/test-list.mjs +202 -203
- package/lib/test-map-perf.d.mts +1 -0
- package/lib/test-map-perf.mjs +70 -0
- package/lib/test-map.mjs +201 -208
- package/lib/test-utils.d.mts +9 -5
- package/lib/test-utils.mjs +131 -18
- package/lib/{main.mjs → test.mjs} +3 -0
- package/lib/types.d.mts +1 -0
- package/package.json +14 -5
- /package/lib/{main.d.mts → test.d.mts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runDetailedListPerformanceTests(): void;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { initTernaryTreeList, initTernaryTreeListFromRange, listGet, assocList, insert, listLen, indexOf, listToItems, prepend, concat, slice, } from "./list.mjs";
|
|
2
|
+
function createTestListData(size) {
|
|
3
|
+
const data = [];
|
|
4
|
+
for (let i = 0; i < size; i++) {
|
|
5
|
+
data.push(i);
|
|
6
|
+
}
|
|
7
|
+
return data;
|
|
8
|
+
}
|
|
9
|
+
function measureTime(name, fn) {
|
|
10
|
+
const start = performance.now();
|
|
11
|
+
const result = fn();
|
|
12
|
+
const end = performance.now();
|
|
13
|
+
const duration = end - start;
|
|
14
|
+
console.log(`${name}: ${duration.toFixed(2)}ms`);
|
|
15
|
+
return [result, duration];
|
|
16
|
+
}
|
|
17
|
+
function repeatTest(times, fn) {
|
|
18
|
+
let totalTime = 0;
|
|
19
|
+
for (let i = 0; i < times; i++) {
|
|
20
|
+
const start = performance.now();
|
|
21
|
+
fn();
|
|
22
|
+
const end = performance.now();
|
|
23
|
+
totalTime += end - start;
|
|
24
|
+
}
|
|
25
|
+
return totalTime / times;
|
|
26
|
+
}
|
|
27
|
+
export function runDetailedListPerformanceTests() {
|
|
28
|
+
console.log("\n=== Detailed List Performance Tests ===");
|
|
29
|
+
console.log("Testing various operations with different list sizes to show optimization benefits\n");
|
|
30
|
+
const sizes = [500, 2000, 8000, 16000];
|
|
31
|
+
for (const size of sizes) {
|
|
32
|
+
console.log(`\n--- Performance Analysis: ${size} elements ---`);
|
|
33
|
+
const testData = createTestListData(size);
|
|
34
|
+
// Test list creation - should benefit from reduced overhead
|
|
35
|
+
const [treeList, creationTime] = measureTime(`Create list (${size})`, () => {
|
|
36
|
+
return initTernaryTreeList(testData);
|
|
37
|
+
});
|
|
38
|
+
// Test range creation
|
|
39
|
+
measureTime(`Range init (${size})`, () => {
|
|
40
|
+
return initTernaryTreeListFromRange(testData, 0, size);
|
|
41
|
+
});
|
|
42
|
+
// Test random access patterns - benefits from cached size calculations
|
|
43
|
+
const accessCount = Math.min(1000, size);
|
|
44
|
+
const indices = Array.from({ length: accessCount }, () => Math.floor(Math.random() * size));
|
|
45
|
+
const avgAccessTime = repeatTest(3, () => {
|
|
46
|
+
for (const idx of indices) {
|
|
47
|
+
listGet(treeList, idx);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
console.log(`Random access avg (${accessCount} ops): ${avgAccessTime.toFixed(2)}ms`);
|
|
51
|
+
// Test sequential access
|
|
52
|
+
const avgSeqTime = repeatTest(3, () => {
|
|
53
|
+
for (let i = 0; i < Math.min(size, 1000); i++) {
|
|
54
|
+
listGet(treeList, i);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
console.log(`Sequential access avg (${Math.min(size, 1000)} ops): ${avgSeqTime.toFixed(2)}ms`);
|
|
58
|
+
// Test structural modifications - heavily benefits from cached calculations
|
|
59
|
+
const modCount = Math.min(100, size / 10);
|
|
60
|
+
const avgAssocTime = repeatTest(3, () => {
|
|
61
|
+
let list = treeList;
|
|
62
|
+
for (let i = 0; i < modCount; i++) {
|
|
63
|
+
const idx = Math.floor(Math.random() * size);
|
|
64
|
+
list = assocList(list, idx, idx * 2);
|
|
65
|
+
}
|
|
66
|
+
return list;
|
|
67
|
+
});
|
|
68
|
+
console.log(`Assoc operations avg (${modCount} ops): ${avgAssocTime.toFixed(2)}ms`);
|
|
69
|
+
// Test insertions
|
|
70
|
+
const insertCount = Math.min(50, size / 20);
|
|
71
|
+
const avgInsertTime = repeatTest(3, () => {
|
|
72
|
+
let list = treeList;
|
|
73
|
+
for (let i = 0; i < insertCount; i++) {
|
|
74
|
+
const idx = Math.min(Math.floor(Math.random() * (size + i)), listLen(list));
|
|
75
|
+
list = insert(list, idx, i + size);
|
|
76
|
+
}
|
|
77
|
+
return list;
|
|
78
|
+
});
|
|
79
|
+
console.log(`Insert operations avg (${insertCount} ops): ${avgInsertTime.toFixed(2)}ms`);
|
|
80
|
+
// Test search operations - benefits from cached property access
|
|
81
|
+
const searchCount = Math.min(100, size / 10);
|
|
82
|
+
const searchValues = testData.slice(0, searchCount);
|
|
83
|
+
const avgSearchTime = repeatTest(3, () => {
|
|
84
|
+
for (const value of searchValues) {
|
|
85
|
+
indexOf(treeList, value);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
console.log(`Search operations avg (${searchCount} ops): ${avgSearchTime.toFixed(2)}ms`);
|
|
89
|
+
// Test slicing - benefits from cached size calculations
|
|
90
|
+
const sliceCount = Math.min(20, size / 50);
|
|
91
|
+
const avgSliceTime = repeatTest(3, () => {
|
|
92
|
+
for (let i = 0; i < sliceCount; i++) {
|
|
93
|
+
const start = Math.floor((Math.random() * size) / 2);
|
|
94
|
+
const end = start + Math.floor((Math.random() * size) / 4);
|
|
95
|
+
slice(treeList, start, Math.min(end, size));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
console.log(`Slice operations avg (${sliceCount} ops): ${avgSliceTime.toFixed(2)}ms`);
|
|
99
|
+
// Iteration performance
|
|
100
|
+
const avgIterTime = repeatTest(3, () => {
|
|
101
|
+
let count = 0;
|
|
102
|
+
for (const item of listToItems(treeList)) {
|
|
103
|
+
count++;
|
|
104
|
+
}
|
|
105
|
+
return count;
|
|
106
|
+
});
|
|
107
|
+
console.log(`Iterator traversal avg (${size} items): ${avgIterTime.toFixed(2)}ms`);
|
|
108
|
+
// Complex operation chains - should show cumulative benefits
|
|
109
|
+
measureTime(`Complex chain (${Math.floor(size / 100)} ops)`, () => {
|
|
110
|
+
let list = treeList;
|
|
111
|
+
const ops = Math.floor(size / 100);
|
|
112
|
+
for (let i = 0; i < ops; i++) {
|
|
113
|
+
// Prepend
|
|
114
|
+
list = prepend(list, -i);
|
|
115
|
+
// Random assoc
|
|
116
|
+
if (listLen(list) > 10) {
|
|
117
|
+
const idx = Math.floor(Math.random() * Math.min(listLen(list), 100));
|
|
118
|
+
list = assocList(list, idx, i * 1000);
|
|
119
|
+
}
|
|
120
|
+
// Random access
|
|
121
|
+
if (listLen(list) > 5) {
|
|
122
|
+
listGet(list, Math.floor(Math.random() * Math.min(listLen(list), 50)));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return list;
|
|
126
|
+
});
|
|
127
|
+
console.log("");
|
|
128
|
+
}
|
|
129
|
+
// Stress test for deep operations
|
|
130
|
+
console.log("=== Stress Tests ===");
|
|
131
|
+
const largeList = initTernaryTreeList(createTestListData(50000));
|
|
132
|
+
measureTime("Deep list access stress", () => {
|
|
133
|
+
const samples = 1000;
|
|
134
|
+
for (let i = 0; i < samples; i++) {
|
|
135
|
+
const idx = Math.floor(Math.random() * 50000);
|
|
136
|
+
listGet(largeList, idx);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
measureTime("Deep list modification stress", () => {
|
|
140
|
+
let list = largeList;
|
|
141
|
+
for (let i = 0; i < 100; i++) {
|
|
142
|
+
const idx = Math.floor(Math.random() * listLen(list));
|
|
143
|
+
list = assocList(list, idx, i * 10000);
|
|
144
|
+
}
|
|
145
|
+
return list;
|
|
146
|
+
});
|
|
147
|
+
// Memory efficiency test
|
|
148
|
+
console.log("\n=== Memory Efficiency Tests ===");
|
|
149
|
+
measureTime("Large tree creation (100k)", () => {
|
|
150
|
+
return initTernaryTreeList(createTestListData(100000));
|
|
151
|
+
});
|
|
152
|
+
// Test concatenation performance
|
|
153
|
+
console.log("\n=== Concatenation Performance ===");
|
|
154
|
+
const list1 = initTernaryTreeList(createTestListData(5000));
|
|
155
|
+
const list2 = initTernaryTreeList(createTestListData(5000));
|
|
156
|
+
const list3 = initTernaryTreeList(createTestListData(5000));
|
|
157
|
+
const list4 = initTernaryTreeList(createTestListData(5000));
|
|
158
|
+
measureTime("Concat 4 large lists (5k each)", () => {
|
|
159
|
+
return concat(concat(concat(list1, list2), list3), list4);
|
|
160
|
+
});
|
|
161
|
+
console.log("\n✅ Performance testing completed!");
|
|
162
|
+
}
|
|
163
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
164
|
+
runDetailedListPerformanceTests();
|
|
165
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runListPerformanceTests(): void;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { initTernaryTreeList, initTernaryTreeListFromRange, listGet, assocList, insert, listLen, indexOf, listToItems, prepend, append, concat, slice, reverse, listMapValues, } from "./list.mjs";
|
|
2
|
+
function createTestListData(size) {
|
|
3
|
+
const data = [];
|
|
4
|
+
for (let i = 0; i < size; i++) {
|
|
5
|
+
data.push(i);
|
|
6
|
+
}
|
|
7
|
+
return data;
|
|
8
|
+
}
|
|
9
|
+
function measureTime(name, fn) {
|
|
10
|
+
const start = performance.now();
|
|
11
|
+
const result = fn();
|
|
12
|
+
const end = performance.now();
|
|
13
|
+
console.log(`${name}: ${(end - start).toFixed(2)}ms`);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
export function runListPerformanceTests() {
|
|
17
|
+
console.log("\n=== List Performance Tests ===");
|
|
18
|
+
const sizes = [100, 1000, 5000, 10000];
|
|
19
|
+
for (const size of sizes) {
|
|
20
|
+
console.log(`\n--- Testing with ${size} elements ---`);
|
|
21
|
+
const testData = createTestListData(size);
|
|
22
|
+
// Test list creation
|
|
23
|
+
const treeList = measureTime(`initTernaryTreeList(${size})`, () => {
|
|
24
|
+
return initTernaryTreeList(testData);
|
|
25
|
+
});
|
|
26
|
+
// Test range creation
|
|
27
|
+
measureTime(`initTernaryTreeListFromRange(${size})`, () => {
|
|
28
|
+
return initTernaryTreeListFromRange(testData, 0, size);
|
|
29
|
+
});
|
|
30
|
+
// Test random access (listGet)
|
|
31
|
+
const indices = Array.from({ length: Math.min(100, size) }, (_, i) => Math.floor(Math.random() * size));
|
|
32
|
+
measureTime(`Random access(${indices.length})`, () => {
|
|
33
|
+
for (const idx of indices) {
|
|
34
|
+
listGet(treeList, idx);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
// Test sequential access
|
|
38
|
+
measureTime(`Sequential access(${size})`, () => {
|
|
39
|
+
for (let i = 0; i < size; i++) {
|
|
40
|
+
listGet(treeList, i);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// Test prepend operations
|
|
44
|
+
measureTime(`Prepend operations(100)`, () => {
|
|
45
|
+
let list = treeList;
|
|
46
|
+
for (let i = 0; i < 100; i++) {
|
|
47
|
+
list = prepend(list, i + size);
|
|
48
|
+
}
|
|
49
|
+
return list;
|
|
50
|
+
});
|
|
51
|
+
// Test append operations
|
|
52
|
+
measureTime(`Append operations(100)`, () => {
|
|
53
|
+
let list = treeList;
|
|
54
|
+
for (let i = 0; i < 100; i++) {
|
|
55
|
+
list = append(list, i + size);
|
|
56
|
+
}
|
|
57
|
+
return list;
|
|
58
|
+
});
|
|
59
|
+
// Test assoc operations
|
|
60
|
+
const assocIndices = Array.from({ length: Math.min(50, size) }, (_, i) => Math.floor(Math.random() * size));
|
|
61
|
+
measureTime(`Assoc operations(${assocIndices.length})`, () => {
|
|
62
|
+
let list = treeList;
|
|
63
|
+
for (const idx of assocIndices) {
|
|
64
|
+
list = assocList(list, idx, idx * 2);
|
|
65
|
+
}
|
|
66
|
+
return list;
|
|
67
|
+
});
|
|
68
|
+
// Test insert operations
|
|
69
|
+
const insertIndices = Array.from({ length: Math.min(25, size) }, (_, i) => Math.floor(Math.random() * (size + i)));
|
|
70
|
+
measureTime(`Insert operations(${insertIndices.length})`, () => {
|
|
71
|
+
let list = treeList;
|
|
72
|
+
for (let i = 0; i < insertIndices.length; i++) {
|
|
73
|
+
const idx = Math.min(insertIndices[i], listLen(list));
|
|
74
|
+
list = insert(list, idx, i + size);
|
|
75
|
+
}
|
|
76
|
+
return list;
|
|
77
|
+
});
|
|
78
|
+
// Test search operations
|
|
79
|
+
const searchValues = testData.slice(0, Math.min(50, size));
|
|
80
|
+
measureTime(`Search operations(${searchValues.length})`, () => {
|
|
81
|
+
for (const value of searchValues) {
|
|
82
|
+
indexOf(treeList, value);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
// Test iteration
|
|
86
|
+
measureTime(`Iterator traversal(${size})`, () => {
|
|
87
|
+
let count = 0;
|
|
88
|
+
for (const item of listToItems(treeList)) {
|
|
89
|
+
count++;
|
|
90
|
+
}
|
|
91
|
+
return count;
|
|
92
|
+
});
|
|
93
|
+
// Test slice operations
|
|
94
|
+
const sliceCount = Math.min(10, size / 10);
|
|
95
|
+
measureTime(`Slice operations(${sliceCount})`, () => {
|
|
96
|
+
for (let i = 0; i < sliceCount; i++) {
|
|
97
|
+
const start = Math.floor((Math.random() * size) / 2);
|
|
98
|
+
const end = start + Math.floor((Math.random() * size) / 4);
|
|
99
|
+
slice(treeList, start, end);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// Test reverse operation
|
|
103
|
+
if (size <= 5000) {
|
|
104
|
+
// Only test reverse for smaller sizes as it can be expensive
|
|
105
|
+
measureTime(`Reverse(${size})`, () => {
|
|
106
|
+
return reverse(treeList);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// Test map operation
|
|
110
|
+
measureTime(`Map operation(${size})`, () => {
|
|
111
|
+
return listMapValues(treeList, (x) => x * 2);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// Concat performance test
|
|
115
|
+
console.log("\n=== Concat Performance Test ===");
|
|
116
|
+
const list1 = initTernaryTreeList(createTestListData(1000));
|
|
117
|
+
const list2 = initTernaryTreeList(createTestListData(1000));
|
|
118
|
+
const list3 = initTernaryTreeList(createTestListData(1000));
|
|
119
|
+
measureTime("Concat two lists(1000 each)", () => {
|
|
120
|
+
return concat(list1, list2);
|
|
121
|
+
});
|
|
122
|
+
measureTime("Concat three lists(1000 each)", () => {
|
|
123
|
+
return concat(concat(list1, list2), list3);
|
|
124
|
+
});
|
|
125
|
+
// Memory stress test
|
|
126
|
+
console.log("\n=== Memory Usage Test ===");
|
|
127
|
+
const largeData = createTestListData(25000);
|
|
128
|
+
measureTime("Large list creation(25k)", () => {
|
|
129
|
+
return initTernaryTreeList(largeData);
|
|
130
|
+
});
|
|
131
|
+
// Cascading operations test
|
|
132
|
+
console.log("\n=== Cascading Operations Test ===");
|
|
133
|
+
measureTime("Complex operations chain", () => {
|
|
134
|
+
let list = initTernaryTreeList(createTestListData(1000));
|
|
135
|
+
// Chain multiple operations
|
|
136
|
+
list = prepend(list, -1);
|
|
137
|
+
list = append(list, 1001);
|
|
138
|
+
list = assocList(list, 500, 9999);
|
|
139
|
+
list = insert(list, 250, 8888);
|
|
140
|
+
// Access and search
|
|
141
|
+
listGet(list, 100);
|
|
142
|
+
indexOf(list, 9999);
|
|
143
|
+
// Slice and iterate
|
|
144
|
+
const sliced = slice(list, 100, 200);
|
|
145
|
+
for (const item of listToItems(sliced)) {
|
|
146
|
+
// consume iterator
|
|
147
|
+
}
|
|
148
|
+
return list;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
152
|
+
runListPerformanceTests();
|
|
153
|
+
}
|