例如:
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++ ;
}
我读过Ada对记录使用Value语义。 上面的Ada循环是否将第k个记录复制到循环变量p? 例如像这样在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++ ;
}
答案 0 :(得分:13)
假设您使用的是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;
所以问题是,renames
如何翻译?鉴于记录大小是2008字节,编译器生成副本的几率几乎为零。
第二种调查方法是将编译器通常发出的汇编代码保存到汇编器,然后使用开关-S
删除。这证实生成的代码就像您的第一个C ++版本(对于macOS)。
作为一个有趣的侧面,Ada 2012允许jobcandidate
的替代实现:
function jobcandidate2 return Boolean is
(for some p of persons => p.age >= 18);
生成相同的代码。
答案 1 :(得分:9)
我怀疑你读到的有关Ada的内容是错误的,可能更糟糕的是,鼓励你以错误的方式思考Ada。
Ada的目的是鼓励在问题领域进行思考,即指明应该发生什么,而不是在解决方案领域进行思考,即实现具体方法的精细细节。
所以这里的目的是遍历所有人,在第一次超过18时退出返回True,否则返回False。
就是这样。
一般而言,只要满足这些语义,Ada就不会对如何完成的细节作出任何规定。
然后,意图是,你只希望编译器做正确的事。
现在一个单独的编译器可以选择一个实现而不是另一个 - 或者可以根据优化启发式在实现之间切换,考虑它正在编译的CPU,以及对象的大小(它们是否适合寄存器?)等
您可以想象一个具有许多寄存器的CPU,其中单个高速缓存行读取使得复制实现比在适当位置操作更快(特别是如果没有修改以回写到P的内容),或其他目标CPU,其中反向是真正。为什么要阻止编译器选择更好的实现?
这方面的一个很好的例子是Ada将参数传递给子程序的方法 - 名称,值或引用语义实际上不适用 - 而是指定参数传递模式 - in
,out
,或in out
描述到子程序(或来自)子程序的信息流。直观,提供可以更严格检查的语义,并使编译器可以自由选择正确服从这些语义的最佳(最快,最小,取决于您的目标)实现。
现在,特定的Ada编译器可能会做出糟糕的选择,而30年前,当计算机根本不足以运行Ada编译器时,您可能会发现在早期版本中,编译器。
但我们现在有三十多年的编译器开发,在更强大的计算机上运行。所以,今天,我希望编译器能够正常地做出最佳选择。如果您发现某个特定编译器错过了性能优化,请提交增强请求。 Ada编译器并不完美,就像任何其他编译器一样。
在这个具体的例子中,我通常希望P成为数组的游标,并且操作就地发生,即引用语义。或者可能是表单之间的混合,其中一个存储器提取到寄存器中用于多个操作,如部分形式的值语义。
如果您的兴趣是学术性的,您可以轻松查看您正在使用的任何编译器的汇编输出并找出答案。或者写上面的所有三个版本并对它们进行基准测试。
答案 2 :(得分:0)
使用当前编译器(GCC 7.0.0),我已使用与std:array<char, 4>
等对应的String( 1..4 )
等将您的源复制到Ada程序和C ++程序。交换机只是{ {1}}用于C ++,-O2
用于Ada,以便使用关于对数组元素的检查访问的类似设置等。
这些是-O2 -gnatp
的结果:
C ++:
jobcandidate
阿达:
movl $_ZN15Loop_Over_Array7personsE+4, %eax
movl $_ZN15Loop_Over_Array7personsE+80320004, %edx
jmp .L3
.L8:
addq $2008, %rax
cmpq %rdx, %rax
je .L7
.L3:
cmpl $17, (%rax)
jle .L8
movl $1, %eax
ret
.L7:
xorl %eax, %eax
ret
我看到的一个区别在于实现如何使用%edx 和%eax ;用于将数组的一个元素转换为下一个元素,并测试是否已达到结束。 Ada似乎 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
设置光标的元素大小,C ++似乎imulq
它指向指针。
我还没有衡量表现。