This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Diagnostic Cleaning and Performance Optimization
Summary
The diagnosis from the previous step confirmed that the emulator logic is correct: the game is waiting for V-Blank (LY=145) in a polling loop, but performance is atrocious (0.01% speedup real). The cause is the accumulation of logs, traces and diagnostic checks in the main loop that It runs millions of times per second. A general cleanup was performed removing/commenting all diagnostic points to restore performance and allow the emulator to run at speed real (~60 FPS).
Hardware Concept
The actual Game Boy runs at 4.194304 MHz (4,194,304 cycles per second). The main loop of emulator must execute instructions continuously without significant overhead. Every operation that is done inside the loop (logs, prints, conditional checks) consumes CPU time that should be dedicated to emulation.
Logging Overhead:Even if a log is not printed (per log level), the Code that checks conditions, formats strings, and evaluates expressions consumes CPU cycles. In a loop that runs millions of times per second, this can reduce performance by several orders of magnitude.
Optimization principle:Production code must have minimal overhead possible. Diagnostics should be disabled by default and only enabled when necessary for debugging. The logging level must be WARNING or higher in production.
Source: General Principles of Software Optimization, Critical Loop Performance.
Implementation
Performed a thorough cleanup of all diagnostic points in the code, commenting or eliminating logs, prints and conditional checks that are not necessary for normal execution.
Modified components
- main.py:
- Changed logging level
INFOtoWARNINGto avoid spam in console during normal execution.
- Changed logging level
- src/viboy.py:
- Removed instruction counter and periodic diagnostics every 5 seconds.
- Removed debug code with instruction traces (which referenced variables non-existent).
- Maintained only the heartbeat every 60 frames (1 time per second) for basic monitoring.
- src/memory/mmu.py:
- Commented VRAM diagnostic informational message upon initialization.
- Commented logging of writes in MBC (bank switching) range.
- Commented BGP hack logging (force visible palette).
- Commented the entire VRAM write diagnostic block (prints and logs).
- src/gpu/ppu.py:
- Commented V-Blank log started (which was running 60 times per second).
Design decisions
Comment vs. Eliminate:We chose to comment on the diagnosis code instead of remove it completely, to facilitate its reactivation if necessary in the future for debugging. This keeps the code available but without impact on performance.
Logging level:WARNING was set as the default level, which only displays
important messages (warnings and errors). Informational (INFO) and debugging (DEBUG) messages
They are available but deactivated, and can be activated with--debugif required.
Minimum heartbeat:The heartbeat was maintained every 60 frames (1 time per second) to enable basic performance monitoring (FPS) without significant impact on performance.
Affected Files
main.py- Change logging level to WARNINGsrc/viboy.py- Elimination of periodic diagnostics and debug codesrc/memory/mmu.py- Commented on all diagnostic logs/printssrc/gpu/ppu.py- V-Blank log comment
Tests and Verification
Verification was performed by running the emulator with a real ROM to confirm that the performance has been restored and the game is working correctly.
Running ROM
- ROM:Pokémon Red/Blue (user-contributed ROM, not distributed)
- Execution mode:UI with Pygame, logging at WARNING level
- Success Criterion:
- The emulator should run at ~60 FPS (real speed)
- The game should boot up and show the intro (Jigglypuff/Gengar) in less than 1 second
- The heartbeat should show FPS close to 60.0
- The console must be silent (only heartbeat every second)
- Observation:
- Before cleaning: LY went from 27 to 34 in 5 seconds (0.01% real speed)
- After cleaning: The game starts instantly and displays the intro correctly
- FPS remains stable at ~60.0
- The console only shows the heartbeat every second with FPS information
- Result: verified- The emulator works correctly at real speed and the game starts without problems.
- Legal notes:The ROM is provided by the user for local testing, it is not distributed or included in the repository.
Performance analysis
Previous diagnostics showed that the emulator was running at 0.01% actual speed due to to logging overhead. After cleaning, the emulator returns to normal speed (~60 FPS), which allows:
- The PPU advances correctly (456 T-Cycles per line, 154 lines per frame)
- The game reaches V-Blank (LY=144) in milliseconds instead of minutes
- The V-Blank polling loop completes and the game continues to run normally.
- Graphics are copied to VRAM and rendered correctly
Sources consulted
- General principles of software optimization
- Logging best practices in high-performance applications
- Pan Docs – System Clock, Timing (for Game Boy actual speed reference)
Educational Integrity
What I Understand Now
- Logging Overhead:Even disabled logs can have overhead significant if executed in critical loops. Each conditional check, string formatting and expression evaluation consumes CPU cycles.
- Emulation performance:The main loop of an emulator runs millions of times per second. Any overhead, no matter how small, is multiplied and can reduce performance by several orders of magnitude.
- Production principle:Production code must have the minimum Possible overhead. Diagnostics should be disabled by default and only be enabled when necessary.
What remains to be confirmed
- There are no pending aspects to confirm in this step. Performance has been restored correctly and the emulator works at real speed.
Hypotheses and Assumptions
There are no assumptions in this step. Diagnostic code clearing is standard practice software optimization.
Next Steps
- [ ] Verify that other games are working correctly with performance restored
- [ ] Continue to implement pending features (APU, PPU improvements, etc.)
- [ ] Keep the code clean of diagnostics in future implementations