在Java,C或C ++中,如何编写未优化的代码以编程方式计算10个整数(从0到9)的总和?
例如,我使用下面的代码,但似乎两个代码(标记为// Baseline和// Method#1的代码)在编译时由编译器优化,并且总变量转换为常量在运行之前。我通过比较相似的time1和time2确认了这一点。
我的问题是:
以编程方式,如何对10个数字求和并强制编译器不优化代码(例如,没有常量传播/折叠)以避免在编译时计算总和,并且强制它仅在运行时发生< /强>
long start, end, time1, time2, total;
//Baseline
start = System.nanoTime();
total = (0+1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9);
end = System.nanoTime();
System.out.println("********************* The sum is " + total);
time1 = end - start;
System.out.println("********************* start=" + start + " end=" + end + " time=" + time1);
//Method #1
start = System.nanoTime();
total = (a0() + a1() + a2() + a3() + a4() + a5() + a6() + a7() + a8() + a9());
end = System.nanoTime();
System.out.println("********************* The sum is " + total);
time2 = end - start;
System.out.println("********************* start=" + start + " end=" + end + " time=" + time2);
}
private int a0()
{
return 0;
}
private int a1()
{
return 1;
}
private int a2()
{
return 2;
}
private int a3()
{
return 3;
}
private int a4()
{
return 4;
}
private int a5()
{
return 5;
}
private int a6()
{
return 6;
}
private int a7()
{
return 7;
}
private int a8()
{
return 8;
}
private int a9()
{
return 9;
}
更新要求:
(1)仅适用于使用Dalvik编译器的Java
(2)只有原始变量,但嵌套函数(方法)是可以接受的
(3)没有波动
(4)没有循环,跳转或等待陈述
顺便说一句,我尝试了下面建议的C和C ++建议,但是它们似乎不适用于Dalvik编译器,它将加法表达式转换为常量。
答案 0 :(得分:0)
只需在编译时禁用优化,就可以很容易地在C中执行此操作。参加这个C程序:
stations
使用#include <stdio.h>
int main(int argc, char *argv[]) {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
printf("%d\n", sum);
return 0;
}
进行编译。 gcc -O0 program.c
告诉GCC不要优化您的代码。如果你看看输出的程序集(可以通过添加-O0
作为编译参数来完成),你可以看到它确实是前10个数字的总和:
-S
您可以看到片段末尾的跳转回到循环的开头,并在循环开始时看到条件跳转以检查...
LBB0_1: ## =>This Inner Loop Header: Depth=1
cmpl $10, -24(%rbp)
jge LBB0_4
## BB#2: ## in Loop: Header=BB0_1 Depth=1
movl -24(%rbp), %eax
addl -20(%rbp), %eax
movl %eax, -20(%rbp)
## BB#3: ## in Loop: Header=BB0_1 Depth=1
movl -24(%rbp), %eax
addl $1, %eax
movl %eax, -24(%rbp)
jmp LBB0_1
...
。如果您查看优化的汇编代码(使用i < 10
进行编译),您可以看到GCC展开循环并在编译时计算总和:
gcc -O3 -S program.c
我不知道你为什么要这样做。
答案 1 :(得分:0)
您可以使用数组(Java中的对象类型),然后在Java 8+中使用IntStream.sum()
。像,
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int sum = IntStream.of(arr).sum();
或强>
int sum = IntStream.range(0, 10).sum();
答案 2 :(得分:0)
godbolt是一个非常酷的在线C ++编译器,可以方便地显示汇编输出。我尝试了gcc,clang和icc,所有这些都将Angular
源代码优化为编译时结果,即使在1 + 2 + 3...
,但如果你编码like this:
-O0
...你可以得到这样的输出(在这种情况下是GCC 5.3.0)......
// Type your code here, or load an example.
int main()
{
register int sum = 1;
sum += 2;
sum += 3;
sum += 4;
sum += 5;
sum += 6;
sum += 7;
sum += 8;
sum += 9;
return sum;
}
显然,每个添加都是单独完成的。这也避免了循环控制的开销,如果你引入了一个仅在运行时已知的边界的for循环,你就会有这种开销。
不过,这是什么意思?在给定相同数量的运行时已知变量的情况下,无法保证编译器将生成类似的程序集....
答案 3 :(得分:0)
你想要的是易变 ....
#include <stdio.h>
int main ( void ) {
volatile int n = 0;
n += 1; n += 2; n += 3; n += 4; n += 5;
n += 6; n += 7; n += 8; n += 9; n += 10;
printf("%d\n", n);
return 0;
}
[编辑] 添加似乎发生在寄存器中,但每次添加有两个动作:
movl -4(%ebp), %eax
addl $2, %eax
movl %eax, -4(%ebp)