mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-22 13:22:16 -05:00
EXCLUDE_OBJECT installs the ExcludeObject move transform, and inside a cancelled object _ignore_move updates Python state without ever calling next_transform.move(). Nothing reaches the toolhead, so the lookahead buffer that normally blocks gcode.run_script never fills, and virtual_sdcard.work_handler runs on its own yields alone -- one reactor.pause per 8192-byte read, about 330 lines apart. The eboard's soft-PWM refresh misses its deadline and the MCU shuts down with "Timer too close". Measured on a Creator 5 Pro: 37 s from "Excluding object" to the shutdown, with 1 Hz status queries answered in 1.30-1.78 s throughout while the main MCU sat at mcu_awake=0.000 and gcodein=0. EXCLUDE_OBJECT_DEFINE is not the cost. The line costs 115 ms through the live klippy and nothing thereafter; three prints of the same file without a cancel complete with bytes_retransmit=0. Removing it removes the object there is to cancel, not the work. tools/gcode-bench holds the harnesses, including the shlex fast path that takes that DEFINE from 91.6 ms to 5.9 ms on the printer -- worth having for large plates, but not this bug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVUMfbNuKUWWV32PBcydwb
43 lines
1.5 KiB
Python
Executable File
43 lines
1.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Measure klipper's gcode dispatch throughput on the move lines of a real file.
|
|
|
|
Only the parse+dispatch half runs: the handlers are no-ops, so the number is a
|
|
floor on what klippy must spend per line before any kinematics happen.
|
|
"""
|
|
import argparse, os, sys, time
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from bench_define import build
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser()
|
|
p.add_argument('--klipper', required=True)
|
|
p.add_argument('--gcode', required=True)
|
|
p.add_argument('--limit', type=int, default=20000)
|
|
p.add_argument('--repeat', type=int, default=3)
|
|
a = p.parse_args()
|
|
|
|
moves = []
|
|
with open(a.gcode, 'r', errors='replace') as f:
|
|
for line in f:
|
|
if line[:2] in ('G1', 'G0') and line[2:3] in (' ', '\n'):
|
|
moves.append(line.rstrip('\n'))
|
|
if len(moves) >= a.limit:
|
|
break
|
|
|
|
gc, eo = build(a.klipper)
|
|
for cmd in ('G0', 'G1', 'G92', 'M204', 'M106', 'M107'):
|
|
gc.register_command(cmd, lambda gcmd: None)
|
|
gc.gcode_handlers = gc.ready_gcode_handlers
|
|
|
|
best = None
|
|
for _ in range(a.repeat):
|
|
t0 = time.perf_counter()
|
|
gc._process_commands(moves, need_ack=False)
|
|
dt = time.perf_counter() - t0
|
|
best = dt if best is None else min(best, dt)
|
|
print('move lines: %d' % len(moves))
|
|
print('parse+dispatch: %.3f s total, %.3f ms/line, %.0f lines/s'
|
|
% (best, best * 1e3 / len(moves), len(moves) / best))
|
|
|
|
main()
|