Re: File I/O Metrics

Mark Evenson <[email protected]> Sat, 5 Nov 2022 19:40:01 +0100
Newsgroups gmane.editors.j.devel
Message-ID <[email protected]>

> On Nov 1, 2022, at 02:33, Alan Ruttenberg <[email protected]> =
wrote:
>=20
> 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.
>=20
> (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 =3D (#"read" channel buffer)
>                 until (eql count -1)
>                 sum count
>                 do (#"position" buffer 0)
>                 ))))
>=20
> 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.
>=20
> In the ABCL source code the files "SimpleArray_*.java" are the =
implementations of the nio buffer backed array types. See =
make_array.java 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.
>=20
> (get-java-field 'java$buffers "active" t)
> -> #<org.armedbear.lisp.Java$Buffers$AllocationPolicy NIO {432E958E}>=20=


Hmmm.  That isn=E2=80=99t 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. =20

The =E2=80=9Cofficial=E2=80=9D way is to use the additional keywords to =
CL:MAKE-ARRAY


=46rom the fine manual =C2=A7 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
:nio-direct.

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

  (or
    (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
storage:

  (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.=20
>=20
> 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=E2=80=99t going to work, hence it is =
preferrable to use the CL:MAKE-ARRAY implementation. =20


After I finish stablizing with abcl-1.9.1 (any day now=E2=80=A6). we can =
start look at optimizing for various read/write patterns.  Other than =
=E2=80=9Cmake it as fast as SBCL=E2=80=9D 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. =20

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