Skip to content

Commit e7ea8c7

Browse files
committed
perf(cpu): speed up event system
ditch `array.sort()` and instead manually keep the array sorted when we insert a new item.
1 parent f2d7b1f commit e7ea8c7

File tree

2 files changed

+59
-18
lines changed

2 files changed

+59
-18
lines changed

src/cpu/cpu.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ describe('cpu', () => {
2626
]);
2727
});
2828

29+
it('should correctly sort the events when added in reverse order', () => {
30+
const cpu = new CPU(new Uint16Array(1024), 0x1000);
31+
const events: ITestEvent[] = [];
32+
for (const i of [10, 4, 1]) {
33+
cpu.addClockEvent(() => events.push([i, cpu.cycles]), i);
34+
}
35+
for (let i = 0; i < 10; i++) {
36+
cpu.cycles++;
37+
cpu.tick();
38+
}
39+
expect(events).toEqual([
40+
[1, 1],
41+
[4, 4],
42+
[10, 10],
43+
]);
44+
});
45+
2946
describe('updateClockEvent', () => {
3047
it('should update the number of cycles for the given clock event', () => {
3148
const cpu = new CPU(new Uint16Array(1024), 0x1000);
@@ -65,6 +82,20 @@ describe('cpu', () => {
6582
[10, 10],
6683
]);
6784
});
85+
86+
it('should return false if the provided clock event is not scheduled', () => {
87+
const cpu = new CPU(new Uint16Array(1024), 0x1000);
88+
const event4 = cpu.addClockEvent(() => 0, 4);
89+
const event10 = cpu.addClockEvent(() => 0, 10);
90+
cpu.addClockEvent(() => 0, 1);
91+
92+
// Both event should be successfully removed
93+
expect(cpu.clearClockEvent(event4)).toBe(true);
94+
expect(cpu.clearClockEvent(event10)).toBe(true);
95+
// And now we should get false, as these events have already been removed
96+
expect(cpu.clearClockEvent(event4)).toBe(false);
97+
expect(cpu.clearClockEvent(event10)).toBe(false);
98+
});
6899
});
69100
});
70101
});

src/cpu/cpu.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -173,23 +173,29 @@ export class CPU implements ICPU {
173173
}
174174
}
175175

176-
private updateClockEvents() {
177-
this.clockEvents.sort((a, b) => a.cycles - b.cycles);
178-
this.nextClockEvent = this.clockEvents[0]?.cycles ?? 0;
179-
}
180-
181176
addClockEvent(callback: AVRClockEventCallback, cycles: number) {
182177
const entry = { cycles: this.cycles + Math.max(1, cycles), callback };
183-
this.clockEvents.push(entry);
184-
this.updateClockEvents();
178+
// Add the new entry while keeping the array sorted
179+
const { clockEvents } = this;
180+
if (!clockEvents.length || clockEvents[clockEvents.length - 1].cycles <= entry.cycles) {
181+
clockEvents.push(entry);
182+
} else if (clockEvents[0].cycles >= entry.cycles) {
183+
clockEvents.unshift(entry);
184+
} else {
185+
for (let i = 1; i < clockEvents.length; i++) {
186+
if (clockEvents[i].cycles >= entry.cycles) {
187+
clockEvents.splice(i, 0, entry);
188+
break;
189+
}
190+
}
191+
}
192+
this.nextClockEvent = this.clockEvents[0].cycles;
185193
return callback;
186194
}
187195

188196
updateClockEvent(callback: AVRClockEventCallback, cycles: number) {
189-
const entry = this.clockEvents.find((item) => item.callback === callback);
190-
if (entry) {
191-
entry.cycles = this.cycles + Math.max(1, cycles);
192-
this.updateClockEvents();
197+
if (this.clearClockEvent(callback)) {
198+
this.addClockEvent(callback, cycles);
193199
return true;
194200
}
195201
return false;
@@ -199,18 +205,22 @@ export class CPU implements ICPU {
199205
const index = this.clockEvents.findIndex((item) => item.callback === callback);
200206
if (index >= 0) {
201207
this.clockEvents.splice(index, 1);
202-
this.updateClockEvents();
208+
this.nextClockEvent = this.clockEvents[0]?.cycles ?? 0;
209+
return true;
203210
}
211+
return false;
204212
}
205213

206214
tick() {
207-
if (this.nextClockEvent && this.nextClockEvent <= this.cycles) {
208-
const clockEvent = this.clockEvents.shift();
209-
clockEvent?.callback();
210-
this.nextClockEvent = this.clockEvents[0]?.cycles ?? 0;
215+
const { nextClockEvent, clockEvents } = this;
216+
if (nextClockEvent && nextClockEvent <= this.cycles) {
217+
clockEvents.shift()?.callback();
218+
this.nextClockEvent = clockEvents[0]?.cycles ?? 0;
211219
}
212-
if (this.interruptsEnabled && this.nextInterrupt >= 0) {
213-
const interrupt = this.pendingInterrupts[this.nextInterrupt];
220+
221+
const { nextInterrupt } = this;
222+
if (this.interruptsEnabled && nextInterrupt >= 0) {
223+
const interrupt = this.pendingInterrupts[nextInterrupt];
214224
avrInterrupt(this, interrupt.address);
215225
if (!interrupt.constant) {
216226
this.clearInterrupt(interrupt);

0 commit comments

Comments
 (0)