• Stefy Lanza (nextime / spora )'s avatar
    ocr: a lane is a page in flight, not a process (v0.3.43) · 6aa4aacd
    Stefy Lanza (nextime / spora ) authored
    The worker protocol was one request, one reply, strictly in order, so pages in
    flight WAS the process count — and a process costs ~600 MB of imported torch.
    That is the whole reason `surya_instances: 64` meant 64 processes, 37 GB and a
    nine-minute stall with the rented GPU idle at 2% KV-cache usage.
    
    In the served modes that memory buys nothing: the recognition VLM is on the pod,
    the local predictor holds no weights (1.7 MB of tokenizer and config on disk —
    surya-2 returns its own boxes, so the detection model is never even fetched),
    and the process rasterises a page and then waits on a socket. So a lane is a
    worker OBJECT inside the process that already exists, with its own predictor and
    its own page, sharing the one torch import. Effective concurrency is
    instances x lanes; `surya_lanes` defaults to 4, served modes only, built on
    demand.
    
    Three things make one process safe to drive concurrently:
    
      - every reply carries the id of the request it answers. Out-of-order replies
        are the point — a short page must not queue behind a dense one — and
        attributing one page's text to another request would be worse than losing
        it, so an unknown id is dropped rather than guessed at;
      - the token trace and the accent repairs are per THREAD. surya's hook is
        module-level, one _generate_one per process, and its sink was a list
        belonging to whichever worker object installed it last. Shared between
        lanes it would have filed one page's logprobs, and surya_repair_accents'
        rewrites, under a different page;
      - a worker that dies fails its pages instead of leaving them parked on a
        reply that is never coming, and killing the engine wakes everything
        waiting.
    
    A single-lane engine sends no ids and reads replies in order exactly as before,
    which is how paddle, docTR and surya-`local` are still driven. `lanes()` is
    asked for politely (_engine_lanes), because an engine is anything with
    recognize_image and a pool must not refuse to build over a capability an engine
    simply does not have.
    
    Tested against the REAL worker protocol with a stubbed engine: four 0.25s pages
    finish in under 0.75s, on four distinct lane objects, and six pages over two
    lanes never exceed two.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01PxYHWbCAjjjD7A1rhFKtaW
    6aa4aacd
ocr.md 62.7 KB