From: Gautier write-only on
Hello.
I got used to think that I/O was the last spot where our preferred
language was condemned to slowness.
Now consider this. Variant 1 of a buffered I/O:

type Buffer is array(Natural range <>) of Unsigned_8;

procedure Read( b: out Buffer ) is
begin
Buffer'Read(Stream(f_in), b);
exception
when Ada.Streams.Stream_IO.End_Error =>
null;
-- Nothing bad, just some garbage in the buffer
-- after end of compressed code
end Read;

procedure Write( b: in Buffer ) is
begin
Buffer'Write(Stream(f_out), b);
end Write;

Bad luck, it is as slow as doing I/O's with single bytes and
Sequential_IO! But if it is slow by receiving/sending a whole buffer,
how to make it faster ? Now someone (in a slightly different context)
came with this (call it variant 2):

procedure Read( b: out Buffer ) is
use Ada.Streams;
First : constant Stream_Element_Offset:= Stream_Element_Offset
(b'First);
Last : Stream_Element_Offset:= Stream_Element_Offset
(b'Last);
SE_Buffer : Stream_Element_Array (First..Last);
begin
Read(Stream(f_in).all, SE_Buffer, Last);
for i in First..Last loop
b(Natural(i)):= Unsigned_8(SE_Buffer(i));
end loop;
end Read;

procedure Write( b: in Buffer ) is
use Ada.Streams;
First : constant Stream_Element_Offset:= Stream_Element_Offset
(b'First);
Last : constant Stream_Element_Offset:= Stream_Element_Offset
(b'Last);
SE_Buffer : Stream_Element_Array (First..Last);
begin
for i in SE_Buffer'Range loop
SE_Buffer(i):= Stream_Element(b(Natural(i)));
end loop;
Write(Stream(f_out).all, SE_Buffer);
end Write;

Naively, you would say it is even slower: you do even more by copying
a buffer into another one, right ?
Indeed, not at all, it is *lots* faster (on GNAT and ObjectAda)!
To give an idea, the variant 1 applied to a bzip2 decompressor makes
it 4x slower than the C version, and variant 2 makes it only 7%
slower! With only I/O (like copying a file) you would get an even much
larger difference.

Now, it raises some questions:
Is there maybe a reason in the RM why the 'Read and 'Write have to be
that slow ?
Or are these two compilers lazy when compiling these attributes ?
Should I bug Adacore about that, then ?
Do some other compilers do it better ?
_________________________________________________________
Gautier's Ada programming -- http://sf.net/users/gdemont/
NB: For a direct answer, e-mail address on the Web site!
From: Jeffrey R. Carter on
Gautier write-only wrote:
>
> Naively, you would say it is even slower: you do even more by copying
> a buffer into another one, right ?
> Indeed, not at all, it is *lots* faster (on GNAT and ObjectAda)!
> To give an idea, the variant 1 applied to a bzip2 decompressor makes
> it 4x slower than the C version, and variant 2 makes it only 7%
> slower! With only I/O (like copying a file) you would get an even much
> larger difference.

And if you overlay the Stream_Element_Array onto the Buffer, thus eliminating
the copying and the stream attribute operations?

--
Jeff Carter
"C's solution to this [variable-sized array parameters] has real
problems, and people who are complaining about safety definitely
have a point."
Dennis Ritchie
25
From: Georg Bauhaus on
On 10/30/09 12:29 AM, Gautier write-only wrote:

> procedure Write( b: in Buffer ) is
> use Ada.Streams;
> First : constant Stream_Element_Offset:= Stream_Element_Offset
> (b'First);
> Last : constant Stream_Element_Offset:= Stream_Element_Offset
> (b'Last);
> SE_Buffer : Stream_Element_Array (First..Last);
> begin
> for i in SE_Buffer'Range loop
> SE_Buffer(i):= Stream_Element(b(Natural(i)));
> end loop;
> Write(Stream(f_out).all, SE_Buffer);
> end Write;
>
> Naively, you would say it is even slower: you do even more by copying
> a buffer into another one, right ?
> Indeed, not at all, it is *lots* faster (on GNAT and ObjectAda)!


I finally thought that the above procedures are faster than 'Read
or 'Write because the latter are defined in terms of stream elements:
When there is a composite object like b : Buffer and you
'Write it, then for each component of b the corresponding 'Write
is called. This then writes stream elements, probably
calling Stream_IO.Write or some such in the end.
So Write from above appears closer to writing bulk loads
of stream elements than a bulk load of 'Writes can be.

Copying buffers does not matter in comparison to the needs
of I/O (on PCs).
From: Gautier write-only on
On 30 Okt., 01:39, "Jeffrey R. Carter"
<spam.jrcarter....(a)spam.acm.org> wrote:

> And if you overlay the Stream_Element_Array onto the Buffer, thus eliminating
> the copying and the stream attribute operations?

With an Unchecked_conversion ?... Sure, but you cannot guarantee that
the Buffer will be packed the same way as Stream_Element_Array on a
compiler X that you don't know. On that type of project (open-source
compression, no compiler/system dependency) I want to avoid any "dirty
trick" like that.
Anyway, the copying takes almost no time. Note that I do not copy
_and_ use the stream attribute at the same time:
Variant 1 uses the stream attribute on Buffer.
Variant 2 copies into a Stream_Element_Array ans uses the Write
procedure in Ada.Streams.

Gautier
From: Gautier write-only on
On 30 Okt., 04:36, Georg Bauhaus <rm-host.bauh...(a)maps.futureapps.de>:

> I finally thought that the above procedures are faster than 'Read
> or 'Write because the latter are defined in terms of stream elements:
> When there is a composite object like b : Buffer and you
> 'Write it, then for each component of b the corresponding 'Write
> is called. This then writes stream elements, probably
> calling Stream_IO.Write or some such in the end.
> So Write from above appears closer to writing bulk loads
> of stream elements than a bulk load of 'Writes can be.

Sure, it is the safe way: write records field by field, arrays element
by element (that recursively). The compiler avoids problems with non-
packed data. Nothing against that. The general case is well done,
fine. But the compiler could have a look a the type left to the
attribute and in such a case (an array of Unsigned_8, or a String)
say: "Gee! that type Buffer is coincidentally the same as
Stream_Element_Array, then I take the shortcut to generate the code to
write the whole buffer and, this time, not the code to write it
element by element".

> Copying buffers does not matter in comparison to the needs of I/O (on PCs).

Right. Variant 2 works fine, but it is an heavy workaround in terms of
source code. Especially for mixed type I/O with plenty of String'Write
and others, you would not want to put the kind of Variant 2 code all
over the place. It would be a lot better that compilers are able to
take selectively the shortcut form for the attributes.

Gautier