Prev: Question on array of strings
Next: Types, packages & objects : the good old naming conventions question (without religious ware)
From: Gautier write-only on 29 Oct 2009 19:29 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 29 Oct 2009 20:39 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 29 Oct 2009 23:36 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 30 Oct 2009 04:58 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 30 Oct 2009 05:13
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 |