Atomic-Pipe
view release on metacpan or search on metacpan
Object sizes 181 .. 1977 bytes, average ~890 B; ~37% of objects under
500 B. Most messages fit in a single PIPE_BUF burst regardless of
compression.
level raw MB/s wire MB ratio saved
plain 9.74 10.00 - -
L-3 15.98 6.68 1.50x 33.2%
L1 24.55 4.92 2.03x 50.8%
L3 (def) 27.79 4.91 2.04x 50.9%
L5 46.34 4.87 2.05x 51.3%
L7 63.72 4.87 2.05x 51.3%
L12 27.02 4.85 2.06x 51.5%
L22 14.43 4.84 2.07x 51.6%
For this size distribution, levels 1..7 are all faster than no
compression (pipe back-pressure on the uncompressed run still
dominates).
Larger JSON (100 MB total, 20407 objects)
Object sizes 187 .. 10000 bytes, average ~5.1 KB, evenly distributed
across the 1..10 KB range. Most objects exceed PIPE_BUF, so the
uncompressed path pays the multi-part fragmentation cost on nearly
every message.
level raw MB/s wire MB ratio saved
plain 0.29 100.00 - -
L-3 287.85 35.61 2.81x 64.4%
L-1 273.56 33.92 2.95x 66.1%
L1 237.04 30.56 3.27x 69.4%
L3 (def) 207.61 30.25 3.31x 69.7%
L5 113.02 30.01 3.33x 70.0%
L9 39.35 29.93 3.34x 70.1%
L18 7.81 28.14 3.55x 71.9%
L22 7.85 28.14 3.55x 71.9%
Here the uncompressed run collapses to ~0.29 MB/s, while even modest
compression levels achieve 200+ MB/s -- a ~1000x throughput
improvement driven almost entirely by avoided fragmentation. Levels
above ~5 trade significant CPU for negligible additional ratio.
Pipe buffer size has minimal impact
The same 100 MB corpus, holding mode constant and varying the kernel
pipe buffer (32 KB, 128 KB, 512 KB, 1 MB), shows almost no movement
in either direction. The bottleneck is PIPE_BUF-aligned framing, not
buffer fill, so calling "resize" with a larger size will not rescue
an uncompressed large-message workload.
Practical guidance
* If your messages are routinely larger than PIPE_BUF (~4 KB),
enabling compression is almost always a throughput win, not just a
bandwidth win.
* For mixed JSON-like payloads, level 1 or the default level 3 are
good starting points. Level -3 is the throughput champion when CPU is
precious and some ratio can be sacrificed.
* Levels above ~7 buy single-digit-percent ratio gains for multi-x
CPU cost; in an IPC path they are rarely worth it.
* A custom dictionary ("Custom dictionary") helps most when payloads
are small and share structure -- e.g. identical JSON keys across
every message.
These results depend heavily on payload entropy and CPU. Re-run
bench/zstd_compression.pl against a representative slice of your own
data before committing to a level.
METHODS
CLASS METHODS
$bytes = Atomic::Pipe->PIPE_BUF
Get the maximum number of bytes for an atomic write to a pipe.
$bool = Atomic::Pipe->HAVE_IO_SELECT
True if IO::Select is available on this system. When available, it is
used by default in fill_buffer() to efficiently wait for pipe
readability instead of relying on blocking sysread() with an EINTR
retry loop.
($r, $w) = Atomic::Pipe->pair
($r, $w) = Atomic::Pipe->pair(%params)
Create a pipe, returns a list consisting of a reader and a writer.
All constructors accept the same optional %params: the compression
options documented in "COMPRESSION", and mixed_data_mode => 1 (see
"MIXED DATA MODE").
$p = Atomic::Pipe->new
$p = Atomic::Pipe->new(%params)
If you really must have a new() method it is here for you to abuse.
The returned pipe has both handles, it is your job to then turn it
into 2 clones one with the reader and one with the writer. It is also
your job to make sure you do not have too many handles floating
around preventing an EOF.
$r = Atomic::Pipe->read_fifo($FIFO_PATH, %params)
$w = Atomic::Pipe->write_fifo($FIFO_PATH, %params)
These 2 constructors let you connect to a FIFO by filesystem path.
The interface difference (read_fifo and write_fifo vs specifying a
mode) is because the modes to use for fifo's are not obvious ('+<'
for reading).
NOTE: THERE IS NO EOF for the read-end in the process that created
the fifo. You need to figure out when the last message is received on
your own somehow. If you use blocking reads in a loop with no loop
exit condition then the loop will never end even after all writers
are gone.
( run in 0.336 second using v1.01-cache-2.11-cpan-7fcb06a456a )