为Ada记录数组循环生成了什么代码?

为Ada记录数组循环生成了什么代码?,ada,Ada,例如: type PERSONCV is record name: String ( 1..4 ); age: Integer; cvtext: String ( 1..2000 ); end record; N: constant := 40000; persons : array ( 1..N ) of PERSONCV; function jobcandidate return Boolean is iscandidate: Boo

例如:

type PERSONCV is
   record
      name: String ( 1..4 );
      age: Integer;
      cvtext: String ( 1..2000 );
   end record;

N: constant := 40000;
persons : array ( 1..N ) of PERSONCV;

function jobcandidate return Boolean is
   iscandidate: Boolean := False;
begin
   for p of persons loop   -- what code is generated for this?
      if p.age >= 18 then
         iscandidate := true;
         exit;
      end if;
   end loop;
   return iscandidate;
end;
在C/C++中,循环部分通常是:

PERSONCV * p; // address pointer
int k = 0;
while ( k < N )
   {
   p = &persons [ k ]; // pointer to k'th record
   if ( p->age >= 18 )...  
   ...
   k++ ;
   }
PERSONCV*p;//地址指针
int k=0;
而(kage>=18)。。。
...
k++;
}
我读到Ada对记录使用值语义。 上面的Ada循环是否将第k个记录复制到循环变量p? e、 g.就像在C/C++中一样:

PERSONCV p; // object/variable
int k = 0;
while ( k < N )
   {
   memcpy ( &p, &persons [ k ], sizeof ( PERSONCV ) ); // copies k'th elem 
   if ( p.age >= 18 )...  
   ...
   k++ ;
   }
PERSONCV p;//对象/变量
int k=0;
而(k=18岁)。。。
...
k++;
}

我怀疑你读到的关于Ada的内容是错误的,可能更糟糕的是,它鼓励你以错误的方式思考Ada

Ada的目的是鼓励在问题域中思考,即指定应该发生什么,而不是在解决方案域中思考,即实施具体方式的细节

因此,这里的目的是循环所有人,在18岁以上的第一次见面时返回True,否则返回False

就这样

总的来说,只要这些语义得到满足,Ada对如何完成的细节没有任何要求

然后,目的是,您只希望编译器做正确的事情

现在,单个编译器可以选择一个实现而不是另一个实现,或者可以根据优化启发法在实现之间切换,考虑编译对象的CPU以及对象的大小(它们是否适合寄存器?)等

您可以想象一个具有多个寄存器的CPU,其中单个缓存线读取使复制实现比就地操作(特别是在没有修改写回p的内容的情况下)或其他目标CPU更快,反之亦然。为什么要停止编译器选择更好的实现

这方面的一个很好的例子是Ada向子程序传递参数的方法—名称、值或引用语义实际上不适用—相反,您可以指定参数传递模式—
in
out
,或
in-out
,描述到(或从)子程序的信息流。直观,提供了可以更严格地检查的语义,并让编译器自由选择正确遵守这些语义的最佳(最快、最小,取决于您的目标)实现

现在,一个特定的Ada编译器可能会做出错误的选择,而30年前,当计算机几乎不足以运行Ada编译器时,在早期版本的编译器中,您可能会发现性能因简单而受到影响

但是我们现在已经有三十多年的编译器开发,运行在更强大的计算机上。因此,今天,我希望编译器通常会做出最佳选择。如果您发现某个特定的编译器缺少性能优化,请提交增强请求。Ada编译器并不完美,就像其他编译器一样

在这个特定的示例中,我通常希望p是数组中的一个游标,操作在适当的位置发生,即引用语义。或者可能是形式之间的混合,其中一个寄存器中的一个内存获取服务于多个操作,如值语义的部分形式


如果您的兴趣是学术性的,那么您可以很容易地查看正在使用的任何编译器的汇编输出,并找到答案。或者写下上面的三个版本并对它们进行基准测试。

假设您正在使用GNAT,有两种调查方法

开关
-gnatG
将重新生成编译器前端将传递给后端的类似于Ada的表示(在任何优化之前)。在这种情况下,我明白了

   function resander__jobcandidate return boolean is
      iscandidate : boolean := false;
      L_1 : label
   begin
      L_1 : for C8b in 1 .. 40000 loop
         p : resander__personcv renames resander__persons (C8b);
         if p.age >= 18 then
            iscandidate := true;
            exit;
         end if;
      end loop L_1;
      return iscandidate;
   end resander__jobcandidate;
所以问题是,
重命名
如何翻译?如果记录大小为2008字节,编译器生成副本的可能性几乎为零

第二种研究方法是保留编译器通常发送给汇编程序的汇编代码,然后使用开关
-S
删除。这证实了生成的代码就像您的第一个C++版本(对于MACOS)。 作为一个有趣的旁白,Ada 2012允许替代实现
jobcandidate

   function jobcandidate2 return Boolean is
     (for some p of persons => p.age >= 18);

生成相同的代码。< /P> < P>使用当前编译器(GCC 7.0.0),我已经复制了艾达程序和C++程序,使用<代码> STD:数组< /代码>等对应于<代码>字符串(1…4)< /C> >等,开关只是C++的代码> -O2,和艾达的<>代码> -O2-GNATP < /代码>,以便在对数组元素的检查访问等方面使用可比较的设置

以下是
求职者的结果

   function jobcandidate2 return Boolean is
     (for some p of persons => p.age >= 18);
C++:

艾达:

我看到的一个区别是两种实现如何使用%edx和%eax;用于从数组的一个元素转到下一个元素,并测试是否已到达终点。艾达似乎在<代码> imulq< /Cord>设置游标的元素大小,C++似乎是代码> Adq它指向指针。< /P>
我还没有衡量性能。

你从哪里读到Ada在这方面使用了值语义?我喜欢替代实现!另请参见。GNAT选项有助于完全回答这个问题。非常有用!非常感谢Simon。RM使复制(重命名)的机会为零,我认为:“重命名_声明是一个声明的示例,它不定义新实体,而是定义现有实体的视图”。因此,
p
在每次迭代中都是
resander\uu persons(C8b)
。但这只是转移了问题。@B98,对于
有限的
对象,情况一定是这样,但我不认为你能分辨出一个疯狂的编译器实现者是否复制了一个无限的对象?顺便说一下,2012年及以后的在线ARMs允许您通过添加
#p{par链接到特定段落
    movl    $1, %eax
    jmp .L5
.L10:
    addq    $1, %rax
    cmpq    $40001, %rax
    je  .L9
.L5:
    imulq   $2008, %rax, %rdx
    cmpl    $17, loop_over_array__persons-2004(%rdx)
    jle .L10
    movl    $1, %eax
    ret
.L9:
    xorl    %eax, %eax
    ret