comp.lang.ada
 help / color / mirror / Atom feed
From: Niklas Holsti <niklas.holsti@tidorum.invalid>
Subject: Re: Performance of records with variant parts
Date: Mon, 22 Mar 2021 20:59:31 +0200	[thread overview]
Message-ID: <ibs7okF5i52U1@mid.individual.net> (raw)
In-Reply-To: <07a56dcc-9e17-49b2-a980-3a5a2d265cedn@googlegroups.com>

On 2021-03-22 19:02, John Perry wrote:
> Hello all
> 
> I encountered a significant penalty when using a record with variant
> parts in gnat community edition, on both MacOS and Linux. Suppose I have
> the following. (Kind of long, sorry.)
> 
> | type Object_Type is ( Sphere, Plane, None );
> | type Surface_Type is ( Shiny, Checkerboard );
> | type Thing_Type(kind: Object_Type := None; surface: Surface_Type := Shiny)
> | is record
> |    case kind is
> |       when None =>
> |          null;
> |       when Sphere =>
> |          radius2: Long_Float := 0.0;
> |          center: Vector;
> |       when Plane =>
> |          offset: Long_Float := 0.0;
> |          norm: Vector;
> |    end case;
> | end record;
> 
> Noticing that the fields are actually the same types, but different
> names, I also tried this:
> 
> | type Thing_Type is record
> |    Kind: Object_Type := None;
> |    Surface: Surface_Type := Shiny;
> |    Size: Long_Float := 0.0;
> |    Pos: Vector;
> | end record;
> 
> This second version doesn't seem a good idea, but compiled output 
> runs 15-30% faster. As far as I can tell***, it's related to this
> test which gets called ~3 million times:
> 
> |       case obj.kind is
> |       when Sphere =>
> |          -- do stuff
> |       when Plane =>
> |          -- do other stuff
> |       when None =>
> |          null;
> |       end case;
> 
> Is there a reason that the record with variant parts runs so much 
> slower? I can make the complete source available if need be.


What is your optimization level? -O2?

After the access to read obj.kind, I would not expect a significant 
slow-down in accessing the discriminant-dependent components of the 
record in each branch of the case statement, because the compiler should 
"know" what the discriminant is, and should not check it again -- at 
least if the record is a constant, as "in" parameters are supposed to be 
(in the absence of aliasing). Moreover, in your case the compiler should 
statically know the offset of each discriminant-dependent component, so 
accessing such a component should not require any overhead compared to a 
record without discriminants.

However, in the past I've seen unexpectedly slow code for copying 
records with discriminants. For example, I once measured a simple "swap" 
operation of this form, where Rec_Type had a single discriminant, with 
about 5 values, all components were of scalar type, and the number of 
discriminant-dependent components was about 4 in each case:

    procedure Swap (This, That : in out Rec_Type)
    is
       X : constant Rec_Type := This;
    begin
       This := That;
       That := X;
    end Swap;

On a SPARC v7 target, this procedure took more than 1000 clock cycles. I 
had a look at the machine code, and there seemed to be a ridiculous 
amount of code to compute how much space should be allocated for X, 
depending on the discriminant of This, and how much data should be 
copied in that initialization and in each of the two assignments. It 
seemed to me that the compiler was using some very general approach to 
code generation for record types with variants, and did not try much to 
simplify simple cases like this, or like your code.

But I don't see any similar variable-initialization or record-copying 
code in your example, so my experience with Swap may not be relevant.

If the compiler is stupidly checking the discriminant of obj for each 
obj-component access, you may be able to speed up the code by creating 
separate sub-procedures for "doing stuff" for each value of the 
discriminant (Sphere, Plane, ...), each having an "in" parameter of a 
constrained sub-type of Thing_Type, for example

    subtype Sphere_Thing is Thing_Type (Kind => Sphere);

    procedure Do_Sphere_Stuff (obj : in Sphere_Thing)
    is ... begin ... end Do_Sphere_Stuff;

    ...
    case obj.kind is
    when Sphere => Do_Sphere_Stuff (obj);
    ...

Of course this adds call overhead, unless the procedures are in-lined -- 
but the possible speed-up from the discriminant-constrained subtype may 
be lost if the compiler's in-lining methods are equally stupid. Perhaps 
worth a try, still.

  parent reply	other threads:[~2021-03-22 18:59 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-22 17:02 Performance of records with variant parts John Perry
2021-03-22 17:32 ` Jeffrey R. Carter
2021-03-22 17:49   ` John Perry
2021-03-22 17:54     ` John Perry
2021-03-22 19:31     ` Jeffrey R. Carter
2021-03-22 22:11       ` John Perry
2021-03-23  9:31         ` Jeffrey R. Carter
2021-03-23 14:27           ` Simon Wright
2021-03-23 23:00           ` John Perry
2021-03-23 23:27             ` Jeffrey R. Carter
2021-03-26 15:38               ` Stephen Leake
2021-03-26 15:44                 ` John Perry
2021-03-30  7:12                   ` Emmanuel Briot
2021-04-01  0:03                     ` John Perry
2021-04-01  6:45                       ` Emmanuel Briot
2021-04-01 13:38                       ` Niklas Holsti
2021-04-02 16:07                         ` John Perry
2021-03-22 17:39 ` Dmitry A. Kazakov
2021-03-22 17:45   ` John Perry
2021-03-22 18:07     ` Dmitry A. Kazakov
2021-03-22 18:23       ` John Perry
2021-03-22 20:30         ` Dmitry A. Kazakov
2021-03-22 18:59 ` Niklas Holsti [this message]
2021-03-22 21:54   ` John Perry
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox