From 82da1ecbc8effc3d376e9047493538b6b015e379 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 11 Nov 2026 19:30:07 -0500 Subject: [PATCH] perf(scripting): #789 give the Windows worker pipe a 1 MiB buffer quota each way With a zero quota an overlapped write stays pending until the peer has read the bytes, so the new blocking writer waited out a peer wake-up per frame (windev closed loop 54k -> 26k evals/s). With a quota the write completes once buffered. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk --- .../WorkerPipeSecurity.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess/WorkerPipeSecurity.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess/WorkerPipeSecurity.cs index 5c491e7ab..07cc6c394 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess/WorkerPipeSecurity.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.OutOfProcess/WorkerPipeSecurity.cs @@ -50,11 +50,20 @@ internal static class WorkerPipeSecurity return security; } + /// + /// The pipe's buffer quota in each direction (lmxopcua#789). With a quota of 0 an overlapped write stays pending + /// until the peer has READ the bytes, so a blocking writer () + /// would wait out a full peer wake-up per frame — measured on windev as half the closed-loop throughput. With a + /// quota a write completes as soon as the bytes are buffered; a peer that stops reading still fills it and blocks + /// the writer, which the heartbeat monitor ends. + /// + internal const int BufferBytes = 1024 * 1024; + /// Creates the single-instance server end of a worker pipe with . /// The random pipe name. /// The ACL (). /// The server stream. public static NamedPipeServerStream CreateServer(string pipeName, PipeSecurity security) => NamedPipeServerStreamAcl.Create(pipeName, PipeDirection.InOut, maxNumberOfServerInstances: 1, - PipeTransmissionMode.Byte, PipeOptions.Asynchronous, inBufferSize: 0, outBufferSize: 0, security); + PipeTransmissionMode.Byte, PipeOptions.Asynchronous, inBufferSize: BufferBytes, outBufferSize: BufferBytes, security); }