Skip to content

Commit

Permalink
[#93] optimization of AccurateFrequencyRunner
Browse files Browse the repository at this point in the history
  • Loading branch information
vbmacher committed Jan 1, 2024
1 parent 3ee1479 commit 19e0b54
Showing 1 changed file with 25 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
public class AccurateFrequencyRunner {
private final static Logger LOGGER = LoggerFactory.getLogger(AccurateFrequencyRunner.class);

private final long slotNanos = SleepUtils.SLEEP_PRECISION;
private final double slotNanos = SleepUtils.SLEEP_PRECISION;
private final double slotMicros = slotNanos / 1000.0;

private final AtomicLong executedCycles = new AtomicLong();
Expand All @@ -55,17 +55,30 @@ public class AccurateFrequencyRunner {
*/
@SuppressWarnings("BusyWait")
public CPU.RunState run(Supplier<Double> getTargetFrequencyKHz, Supplier<CPU.RunState> runInstruction) {
final int cyclesPerSlot = Math.max(1, (int) (slotMicros * getTargetFrequencyKHz.get() / 1000.0));
// We need to compensate for 0
final double cyclesPerSlot = Math.max(1, slotMicros * getTargetFrequencyKHz.get() / 1000.0);

LOGGER.debug("Running CPU with {} cycles per slot", cyclesPerSlot);

CPU.RunState currentRunState = CPU.RunState.STATE_RUNNING;
long delayNanos = slotNanos;
long targetCycles = (long) Math.ceil(cyclesPerSlot);

executedCycles.set(0);

long emulationStartTime = System.nanoTime();
while (!Thread.currentThread().isInterrupted() && (currentRunState == CPU.RunState.STATE_RUNNING)) {
double computationStartTime = System.nanoTime();
while ((executedCycles.get() < targetCycles) &&
!Thread.currentThread().isInterrupted() &&
(currentRunState == CPU.RunState.STATE_RUNNING)) {
currentRunState = runInstruction.get();
}

double computationTime = System.nanoTime() - computationStartTime;

// Delay is actually the thing which "moves" the emulation forwards. Since the host CPU is much faster than
// the emulated one, th computationTime << slotNanos. Hence, we will have additional time we haven't used yet,
// to execute additional cycles, which will - again - run in a slotNanos time slot. And the situation repeats.
long delayNanos = (long) (slotNanos - computationTime);

try {
if (delayNanos > 0) {
// We do not require precise sleep here!
Expand All @@ -76,25 +89,15 @@ public CPU.RunState run(Supplier<Double> getTargetFrequencyKHz, Supplier<CPU.Run
break;
}

long computationStartTime = System.nanoTime();
double slotEndTime = System.nanoTime();

// We take into consideration real sleep time
long targetCycles = (computationStartTime - emulationStartTime) / slotNanos * cyclesPerSlot;

// overflow?
if (targetCycles < 0 || executedCycles.get() < 0) {
targetCycles = Math.abs(targetCycles % Long.MAX_VALUE);
executedCycles.updateAndGet(c -> Math.abs(c % Long.MAX_VALUE));
}

while ((executedCycles.get() < targetCycles) &&
!Thread.currentThread().isInterrupted() &&
(currentRunState == CPU.RunState.STATE_RUNNING)) {
currentRunState = runInstruction.get();
}
final long tc = targetCycles;
executedCycles.updateAndGet(c -> c - tc);

long computationTime = System.nanoTime() - computationStartTime;
delayNanos = slotNanos - computationTime;
// After the slot is finished, we know not enough cycles were performed. Hence, we need to compensate
// with additional cycles - target cycles.
// We do not enforce precise sleep here, because we will take into account real sleep time.
targetCycles = (long) Math.ceil((slotEndTime - computationStartTime) / slotNanos * cyclesPerSlot);
}
return currentRunState;
}
Expand Down

0 comments on commit 19e0b54

Please sign in to comment.