The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
NAME
    IO::Vectored - Read from or write to multiple buffers at once

WRITE SYNOPSIS
        use IO::Vectored;

        syswritev($file_handle, "hello", "world") || die "syswritev: $!";

READ SYNOPSIS
        use IO::Vectored;

        my $buf1 = " " x 5;
        my $buf2 = " " x 5;

        sysreadv($file_handle, $buf1, $buf2) || die "sysreadv: $!";

        ## if input is "abcdefg" then:
        ##  $buf1 eq "abcde"
        ##  $buf2 eq "fg   "

DESCRIPTION
    Vectored-IO is sometimes called "scatter-gather" IO. The idea is that
    instead of doing multiple read(2) or write(2) system calls for each
    string, your program creates a vector of pointers to all the strings you
    wish to read/write and then does a single system call referencing this
    vector.

    Although some people consider these interfaces contrary to the
    minimalist design principles of unix, vectored-IO has certain advantages
    which are described below.

    This module is an interface to your system's readv(2)
    <http://pubs.opengroup.org/onlinepubs/009695399/functions/readv.html>
    and writev(2)
    <http://pubs.opengroup.org/onlinepubs/009695399/functions/writev.html>
    vectored-IO system calls specified by POSIX.1. It exports the functions
    "syswritev" and "sysreadv" which are almost the same as the "syswrite"
    and "sysread" perl functions except that they accept multiple strings
    and always have default length and offset parameters.

ADVANTAGES
    The first advantage of vectored-IO is that it reduces the number of
    system calls required. This provides an atomicity guarantee in that your
    reads/writes won't be intermingled with the reads/writes of other
    processes when you aren't expecting it and also eliminates a constant
    overhead particular to your system.

    Another advantage of vectored-IO is that doing multiple system calls can
    result in excess network packets being sent. The classic example of this
    is a web-server sending a static file. If the server "write()"s the HTTP
    headers and then "write()"s the file data, the kernel might send the
    headers and file in separate network packets. Ensuring a single packet
    is better for latency and bandwidth consumption. TCP_CORK
    <http://baus.net/on-tcp_cork/> is a solution to this issue but it is
    Linux-specific and requires more system calls.

    Of course an alternative to vectored-IO is to copy the buffers together
    into a contiguous buffer before calling write(2). The performance
    trade-off of this is that a potentially large buffer needs to be
    allocated and then all the smaller buffers copied into it. Also, if your
    buffers are backed by memory-mapped files (created with File::Map for
    instance) then this approach results in an unnecessary copy of the data
    to userspace. If you use vectored-IO then files can be copied directly
    from the file-system cache into the socket's mbuf
    <http://www.openbsd.org/cgi-bin/man.cgi?query=mbuf>.

    Note that as with anything the performance benefits of vectored-IO will
    vary from application to application and you shouldn't retro-fit it onto
    an application unless benchmarking has shown measurable benefits.
    However, vectored-IO can sometimes be more programmer-convenient than
    regular IO and may be worth using for that reason alone.

RETURN VALUES AND ERROR CONDITIONS
    As mentioned above, this module's interface tries to match "syswrite"
    and "sysread" so the same caveats that apply to those functions apply to
    the vectored interfaces. In particular, you should not mix these calls
    with userspace-buffered interfaces such as "print" or "seek". Mixing the
    vectored interfaces with "syswrite" and "sysread" is fine though.

    "syswritev" returns the number of bytes written (usually the sum of the
    lengths of all arguments). If it returns less, either there was an error
    which is indicated in $! or you are using non-blocking IO in which case
    it is up to you to adjust it so that the next "syswritev" points to the
    remaining data.

    "sysreadv" returns the number of bytes read up to the sum of the lengths
    of all arguments. Note that unlike "sysread", "sysreadv" will not
    truncate any buffers (see the "READ SYNOPSIS" above and the TODO below).

    Both of these functions can also return "undef" if the underlying
    readv(2) or writev(2) system calls fail for any reason other than
    "EINTR". When undef is returned, $! will be set with the error.

    Like "sysread"/"syswrite", the vectored versions also croak for various
    reasons such as passing in too many arguments (more than
    "IO::Vectored::IOV_MAX"), trying to use a closed file-handle, or trying
    to write to a read-only/constant string. See the "t/exceptions.t" test
    for a full list.

    Although not specific to vectored-IO, when accessing "mmap()"ed memory,
    a SIGBUS signal can kill your process if another process truncates the
    backing file while you are accessing it.

SENDFILE
    The non-standard sendfile(2) system call can do one less copy than
    vectored-IO because the file data can be copied directly from the
    filesystem cache into the final network packet if the hardware and
    network driver support scatter-gather IO.

    Another advantage of "sendfile()" is that the file pages are never
    actually mapped into virtual address space. Because the network hardware
    can gather the data from the OS file-system cache with Direct Memory
    Access (DMA), there is less pressure on the Translation Lookaside Buffer
    (TLB). The consequence is less CPU usage per page transferred.

    With "sendfile()", the number of bytes to send with each system call is
    specified by "size_t" so you still have to call it multiple times in
    order to send files too large to map into virtual memory on 32-bit
    systems. However, "sendfile()" doesn't require re-mmaping large files
    throughout the send like vectored-IO does on 32 bit systems so it can
    send files in the fewest number of system calls.

    A good rule of thumb is that "sendfile()" is best for large files and
    vectored-IO is best for small files.

    Unfortunately, where operating systems have implemented it at all, the
    "sendfile()" interfaces are different. The rest of this section will
    briefly describe the pros and cons of some implementations.

    Linux has the most limited "sendfile()" implementation. On Linux, a
    system call is required for each file to be sent, unlike with
    vectored-IO. Also, a solution such as "TCP_CORK" is needed to avoid
    excess network packets. If you are using 32 bit "off_t" then you will
    need the sendfile64(2) transitional interface. Note that while this
    function lets you send large files, you still need to call "sendfile()"
    multiple times since the amount you wish to send at once is stored in a
    "size_t".

    FreeBSD's "sendfile()" allows you to specify leading or trailing
    vectored-IO in addition to the file. This mostly gets rid of the need
    for "TCP_CORK"-like solutions. Sending multiple files in one system call
    is possible but only by taking advantage of one of the vectored-IO
    parameters in which case you must choose one and only one of the files
    to get the advantages of "sendfile()". FreeBSD's "off_t" is always 64
    bits so there is no need for a "sendfile64()".

    As well as a Linux-like "sendfile()", Solaris has a fully vectorised
    interface called "sendfilev()" which allows the arbitrary mixing of
    files and in-process memory buffers. Although in many ways this is the
    best of both worlds, it still doesn't guarantee atomicity like standard
    vectored-IO does. Note that Solaris also provides "sendfile64()" and
    "sendfilev64()" interfaces because "off_t" can be 32 or 64 bits so an
    explicitly 64-bit transitional interface is required.

    As if all the above caveats weren't enough, many "sendfile()"
    implementations will only work when sending from a file (obviously) and
    to a network socket (less obviously). So in order for your code to be
    fully general and portable you may have to implement one code path that
    uses "sendfile()" and one that doesn't.

    How are those minimalist design principles of unix sounding now? :)

TODO
    Consider truncating input strings like "sysread" does. Please don't
    depend on the non-truncating behaviour of "sysreadv" until version
    1.000.

    Implement some helper utilities to make using non-blocking IO easier
    such as a utility to shift off N bytes from the beginning of a vector.

    To the extent possible, make it do the right thing for file-handles with
    non-raw encodings and unicode strings. Any test-cases are appreciated.

    Investigate if there is a performance benefit in eliminating the perl
    subs and re-implementing their logic in XS.

    Think about whether this module should support vectors larger than
    "IOV_MAX" by calling "writev"/"readv" multiple times. This should be
    opt-in because it breaks the atomicity guarantee.

    Support windows with "ReadFileScatter", "WriteFileGather", "WSASend",
    and "WSARecv".

SEE ALSO
    IO-Vectored github repo <https://github.com/hoytech/IO-Vectored>

    Useful modules to combine with vectored-IO:

    File::Map / Sys::Mmap

    overload::substr

    String::Slice

    Even though "sendfile()" is a solution to a somewhat different problem
    (see the SENDFILE section above), here are some perl interfaces:

    Sys::Sendfile / Sys::Syscall / Sys::Sendfile::FreeBSD

AUTHOR
    Doug Hoyte, "<doug@hcsw.org>"

COPYRIGHT & LICENSE
    Copyright 2013-2014 Doug Hoyte.

    This module is licensed under the same terms as perl itself.