From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.5-pre1 (2020-06-20) on ip-172-31-74-118.ec2.internal X-Spam-Level: X-Spam-Status: No, score=-1.9 required=3.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.5-pre1 Path: eternal-september.org!reader02.eternal-september.org!news.swapon.de!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail From: Niklas Holsti Newsgroups: comp.lang.ada Subject: Re: Performance of records with variant parts Date: Mon, 22 Mar 2021 20:59:31 +0200 Organization: Tidorum Ltd Message-ID: References: <07a56dcc-9e17-49b2-a980-3a5a2d265cedn@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit X-Trace: individual.net 8+X40mHZR9bUu3vquEIZCQccfCjPuagVfanQfKNGwWUaDTB6pf Cancel-Lock: sha1:S+TwshgCH5ppugPuIQI/3SmQoKo= User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:78.0) Gecko/20100101 Thunderbird/78.7.0 In-Reply-To: <07a56dcc-9e17-49b2-a980-3a5a2d265cedn@googlegroups.com> Content-Language: en-US Xref: reader02.eternal-september.org comp.lang.ada:61638 List-Id: 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.