Atomic-Pipe

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN


      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 )