# Jak se picows stal nejrychlejším WebSocket motorem pro asyncio: architektura bez kompromisů
Knihovna picows přepsala pravidla výkonu WebSocketů v Pythonu tím, že se vzdala pohodlí ve prospěch rychlosti. Místo async for a automatického sestavování zpráv nabízí přímý přístup k rámcům přes callback rozhraní, zero-copy zpracování a plnou kontrolu nad buffery. Pro algoritmický trading, kde každá milisekunda stojí peníze, to není optimalizace – to je nutnost.
Proč standardní řešení nestačí
Když jde o vysokofrekvenční zpracování dat – například aktualizace z kryptoburz – aiohttp a websockets vykazují nepřijatelné zpoždění. Při zátěži 10–20 tisíc zpráv za sekundu (každá po 200 bajtů) začnou hromadit data ve vnitřních frontách. Zpoždění mezi přijetím paketu ze sítě a jeho doručením do business logiky může dosáhnout 100 ms – fatální pro obchodní strategie.
Problém není v Pythonu jako takovém, ale v architektuře:
- Data se opakovaně kopírují: ze soketu → do bufferu parseru → do fronty → do uživatelské korutiny.
- Parsery běží v Pythonu (kromě částečně přepsaného C-parseru v aiohttp), což vytváří režii.
- TEXT zprávy se konvertují na str, což vyžaduje další alokaci paměti.
- Odesílání velkých zpráv (např. 1 MB) vyžaduje kopírování celého payloadu jen kvůli přidání 2–14 bajtů hlavičky.
To není jen pomalé – je to předvídatelně neefektivní. Zejména při použití asyncio.Protocol, který vždy předává nové objekty bytes místo rychlejšího BufferedProtocol, který umožňuje práci s externím bufferem.
Architektura picows: zero-copy a minimální abstrakce
Picows je postavený na principech, které by v jiných knihovnách považovaly za příliš nízkou úroveň:
- Používá asyncio.BufferedProtocol pro minimalizaci kopírování při čtení.
- Parser v C rozkládá hlavičku rámce a ihned předává hranice payloadu do uživatelského kódu – bez vytváření meziproduktů.
- Uživatel pracuje přímo s memoryview nebo bytearray – bez konverze na str, pokud není potřeba.
- Odesílání je realizováno přes send_reuse_external_bytearray – hlavička se zapíše přímo do rezervovaného prostoru před payloadem, maskování se aplikuje in-place.
Vzdání se pohodlí nebylo snadné, ale je oprávněné:
- Žádné async for – místo toho callback on_ws_frame.
- Žádná automatická sestava multi-frame zpráv – uživatel sám rozhodne, jak je spojit.
- Žádná vestavěná podpora permessage-deflate – komprese se musí řešit na úrovni aplikace.
- send() není async – pokud je socket obsazený, data jdou do fronty bez čekání.
To není bug, ale feature: volání async funkce na každém rámu přidává desítky mikrosekund zpoždění. Picows umožňuje zpracovávat rámy bez přepínání kontextu asyncio.
Příklady kódu: klient a server na picows
Minimální klient:
import asyncio
from picows import ws_connect, WSFrame, WSTransport, WSListener, WSMsgType, WSCloseCode
class ClientListener(WSListener):
def on_ws_connected(self, transport: WSTransport):
transport.send(WSMsgType.TEXT, b"Hello world")
def on_ws_frame(self, transport: WSTransport, frame: WSFrame):
print(f"Echo reply: {frame.get_payload_as_ascii_text()}")
transport.send_close(WSCloseCode.OK)
transport.disconnect()
async def main():
transport, client = await ws_connect(ClientListener, "ws://127.0.0.1:9001")
await transport.wait_disconnected()
if __name__ == '__main__':
asyncio.run(main())
Echo server:
import asyncio
from picows import ws_create_server, WSFrame, WSTransport, WSListener, WSMsgType, WSUpgradeRequest
class ServerClientListener(WSListener):
def on_ws_connected(self, transport: WSTransport):
print("New client connected")
def on_ws_frame(self, transport: WSTransport, frame: WSFrame):
if frame.msg_type == WSMsgType.CLOSE:
transport.send_close(frame.get_close_code(), frame.get_close_message())
transport.disconnect()
else:
transport.send(frame.msg_type, frame.get_payload_as_memoryview())
async def main():
def listener_factory(r: WSUpgradeRequest):
return ServerClientListener()
server: asyncio.Server = await ws_create_server(listener_factory, "127.0.0.1", 9001)
for s in server.sockets:
print(f"Server started on {s.getsockname()}")
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main())
Všimněte si: žádné await uvnitř on_ws_frame. Zpracování probíhá synchronně bez přepínání kontextu. Pokud potřebujete asynchronní chování – můžete použít create_task nebo Queue, ale to je vědomá volba vývojáře, ne vnucená architektura.
Benchmarky: kde picows nechává konkurenty pozadu
Testy ukazují rozdíl v RPS (requests per second) při echo výměně zpráv různé velikosti:
- Pro malé zprávy (64–512 bajtů) picows překonává aiohttp 3–5×, websockets 8–10×.
- Pro velké zprávy (1 MB+) je výhoda ještě větší – až 20× díky zero-copy odesílání.
- Zpoždění mezi přijetím a zpracováním rámce – stabilně pod 100 mikrosekund i při zátěži 50K RPS.
Klíčové faktory:
- Absence zbytečného kopírování – data se čtou a píšou in-place.
- Minimální tvorba objektů – žádné meziprodukty str, list, dict.
- Nulová abstrakce při zpracování rámců – callback se volá hned po parsování hlavičky.
- Optimalizovaný C kód – celé parsování a tvorba rámců běží v C bez zásahu Python interpretu.
Co je důležité
- Picows není pro každého – je pro ty, kteří potřebují maximální výkon, ne pohodlné API.
- Architektura vychází z transportního modelu asyncio: transport/protocol, ne z vysoceúrovňových abstrakcí.
- Zero-copy zpracování a direct memory access – klíčové výhody oproti konkurenci.
- Vzdání se async v hot-path snížilo zpoždění o řády.
- Podpora TLS a složitých scénářů (multi-frame, masking) bez ztráty rychlosti.
Kdy volit picows, a kdy ne
Používejte picows, pokud:
- Je pro vás kritická latence < 1 ms při zpracování tisíců zpráv za sekundu.
- Jste ochotni obětovat pohodlí kvůli kontrole nad pamětí a výkonem.
- Vaše logika lze realizovat bez neustálého používání async/await v obsluze rámců.
- Pracujete s binárními daty nebo JSON, který lze parsovat přímo z memoryview.
Nepoužívejte, pokud:
- Je pro vás důležitá jednoduchost API a rychlý start.
- Očekáváte automatickou sestavu multi-frame zpráv.
- Potřebujete vestavěnou podporu komprese nebo WebSocket rozšíření.
- Vaše zátěž je nízká (< 1K RPS) a nechcete komplikovat architekturu.
Picows není náhradou za aiohttp nebo websockets. Je to specializovaný nástroj pro edge-case scénáře, kde jede o každý takt procesoru. Pro algoritmický trading, herní servery, IoT brány a high-frequency data pipelines – jedna z nejlepších voleb v Python ekosystému.
— Editorial Team
Zatím žádné komentáře.