File I/O Metrics

Mark Evenson evenson at
Sat Nov 5 18:40:01 UTC 2022

> On Nov 1, 2022, at 02:33, Alan Ruttenberg <alanruttenberg at> wrote:
> Here's an example of fast reading in ABCL. The implementation of certain array types use java.nio buffers, which can be directly read into via the java.nio functions.
> (defun test-read (path)
>   (let* ((f (new 'RandomAccessFile (namestring (truename path)) "r"))
>          (channel (#"getChannel" f))
>          (array (make-array (* 16 1024 1024) :element-type '(unsigned-byte 8)))
>          (buffer (get-java-field array "elements" t)))
>     (time (loop for count = (#"read" channel buffer)
>                 until (eql count -1)
>                 sum count
>                 do (#"position" buffer 0)
>                 ))))
> On my machine, for a 5G file, the SBCL code in an earlier post takes 2.4 seconds. This code takes 1.4 sec. It's fastest if I use 2M buffers - 1.1 seconds. SBCL is also marginally faster with smaller buffer sizes.
> In the ABCL source code the files "SimpleArray_*.java" are the implementations of the nio buffer backed array types. See where the specific type of underlying array is chosen. There is a global switch for array allocation choosing either direct allocation or nio-buffers, with the default being nio buffers.
> (get-java-field 'java$buffers "active" t)
> -> #<org.armedbear.lisp.Java$Buffers$AllocationPolicy NIO {432E958E}> 

Hmmm.  That isn’t quite the official interface that I implemented for abcl-1.7. and am rather unsure if setting that really works at the moment.   I would need to look closer at the Java implementation to be sure.  

The “official” way is to use the additional keywords to CL:MAKE-ARRAY

From the fine manual § 4.10 "Extension to CL:MAKE-ARRARY"

With the NIO feature is present and indicated by the presence of
:nio in CL:*FEATURES*, the implementation adds two
keyword arguments to CL:MAKE-ARRAY :nio-buffer and

With the :nio-buffer keyword, the user is able to pass
instances of of java.nio.ByteBuffer and its subclasses for the
storage of vectors and arrays specialized on the byte-vector
types satisfying

    (unsigned-byte 8)
    (unsigned-byte 16)
    (unsigned-byte 32))

As an example, the following would use the :nio-buffer as
follows to create a 16 byte vector using the created byte-buffer for

  (let* ((length 16)
         (byte-buffer (java:jstatic "allocate" "java.nio.ByteBuffer" length)))
    (make-array length :element-type '(unsigned-byte 8) :nio-buffer byte-buffer))

:nio-buffer NIO-BUFFER

Initializes the contents of the new vector or array with the contents
of NIO-BUFFER which needs to be a reference to a
JAVA-OBJECT of class java.nio.ByteBuffer.

\:nio-direct NIO-DIRECT-P

NIO-DIRECT-P is not NIL, constructs a
java.nio.Buffer as a ``direct'' buffer.  The buffers returned by this
method typically have somewhat higher allocation and deallocation
costs than non-direct buffers. The contents of direct buffers may
reside outside of the normal garbage-collected heap, and so their
impact upon the memory footprint of an application might not be
obvious. It is therefore recommended that direct buffers be allocated
primarily for large, long-lived buffers that are subject to the
underlying system's native I/O operations. In general it is best to
allocate direct buffers only when they yield a measurable gain in
program performance.

> I haven't looked into the :element-type 'character case. 
> Maybe someone who is familiar with the ABCL stream implementation is interested in writing a fast path for read-sequence that uses the nio calls? If so, shout. Otherwise I'll keep it on my procrastinate-by-hacking-abcl list.

I have some experiments with replacing i/o with asynchronous thread pools, for which using specials isn’t going to work, hence it is preferrable to use the CL:MAKE-ARRAY implementation.  

After I finish stablizing with abcl-1.9.1 (any day now…). we can start look at optimizing for various read/write patterns.  Other than “make it as fast as SBCL” it would be helpful if potential users could give me some usage patterns to optimize for.

Oh, and with suitable elbow grease the ultimate plan is to add a flag to mmap(2) files as well, which should speed up fasl loads immensely.  

"A screaming comes across the sky.  It has happened before but there is nothing 
to compare to it now."

More information about the armedbear-devel mailing list