我一直在绞尽脑汁想要完成这项任务一周,我希望有人能带领我走向正确的道路.让我从教师的指示开始:
您的作业与我们的第一个实验作业相反,即优化素数计划.你在这个任务中的目的是使程序失望,即让它运行得更慢.这两个都是CPU密集型程序.他们需要几秒钟才能在我们的实验室电脑上运行.您可能无法更改算法.
要取消优化程序,请使用您对英特尔i7管道如何运行的了解.想象一下重新排序指令路径以引入WAR,RAW和其他危险的方法.想一想最小化缓存有效性的方法.恶魔无能.
该作业选择了Whetstone或Monte-Carlo程序.缓存有效性评论大多只适用于Whetstone,但我选择了Monte-Carlo模拟程序:
// Un-modified baseline for pessimization, as given in the assignment
#include <algorithm> // Needed for the "max" function
#include <cmath>
#include <iostream>
// A simple implementation of the Box-Muller algorithm, used to generate
// gaussian random numbers - necessary for the Monte Carlo method below
// Note that C++11 actually provides std::normal_distribution<> in
// the <random> library, which can be used instead of this function
double gaussian_box_muller() {
double x = 0.0;
double y = 0.0; …
Run Code Online (Sandbox Code Playgroud) 更新,见下文!
我听说并读过C++ 0x允许编译器为以下代码段打印"Hello"
#include <iostream>
int main() {
while(1)
;
std::cout << "Hello" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它显然与线程和优化功能有关.在我看来,这可能让许多人感到惊讶.
有人能够很好地解释为什么必须允许这样做吗?作为参考,最新的C++ 0x草案说明了6.5/5
在for语句的情况下,在for-init语句之外的循环,
- 不调用库I/O函数,和
- 不访问或修改易失性对象,以及
- 不执行同步操作(1.10)或原子操作(第29条)
可以通过实现来假设终止.[注意:这是为了允许编译器转换,例如删除空循环,即使无法证明终止也是如此. - 结束说明]
编辑:
这篇富有洞察力的文章谈到了标准文本
不幸的是,没有使用"未定义的行为".但是,只要标准说"编译器可以假设P",就暗示具有非-P属性的程序具有未定义的语义.
这是正确的,是否允许编译器为上述程序打印"Bye"?
这里有一个更有见地的线索,这是关于C的类似改变,由Guy完成上述链接文章开始.在其他有用的事实中,他们提出了一个似乎也适用于C++ 0x的解决方案(更新:这将不再适用于n3225 - 见下文!)
endless:
goto endless;
Run Code Online (Sandbox Code Playgroud)
看来,编译器不允许优化它,因为它不是循环,而是跳转.另一个人总结了C++ 0x和C201X的建议更改
通过编写一个循环,程序员断言或者环路不可见的东西的行为(执行I/O,访问volatile对象,或执行同步或原子操作), 或者,它最终会终止.如果我通过编写一个没有副作用的无限循环来违反这个假设,我对编译器撒谎,而我的程序的行为是未定义的.(如果我很幸运,编译器可能会警告我.)语言不提供(不再提供?)表达无可见行为的无限循环的方法.
2011年3月3日更新为n3225:委员会将案文移至1.10/24并说
实现可以假定任何线程最终将执行以下操作之一:
- 终止,
- 调用库I/O函数,
- 访问或修改易失性对象,或
- 执行同步操作或原子操作.
的goto
把戏,不工作了!
C11 标准似乎暗示不应优化带有常量控制表达式的迭代语句。我从这个答案中得到了我的建议,它特别引用了标准草案中的第 6.8.5 节:
其控制表达式不是常量表达式的迭代语句......可能会被实现假定为终止。
在该答案中,它提到while(1) ;
不应进行优化之类的循环。
那么……为什么 Clang/LLVM 优化了下面的循环(用 编译cc -O2 -std=c11 test.c -o test
)?
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
die();
printf("unreachable\n");
}
Run Code Online (Sandbox Code Playgroud)
在我的机器上,这会打印出begin
,然后在非法指令(ud2
放置在 之后的陷阱die()
)上崩溃。在 Godbolt 上,我们可以看到调用puts
.
让 Clang 输出无限循环是一项非常困难的任务-O2
- 虽然我可以反复测试一个volatile
变量,但这涉及到我不想要的内存读取。如果我做这样的事情:
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
volatile int x …
Run Code Online (Sandbox Code Playgroud) 背景
我被一位朋友问过以下谜题:
void fn(void)
{
/* write something after this comment so that the program output is 10 */
/* write something before this comment */
}
int main()
{
int i = 5;
fn();
printf("%d\n", i);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我知道可以有多个解决方案,一些涉及宏,一些假设有关实现和违反C.
我感兴趣的一个特定解决方案是对堆栈做出某些假设并编写以下代码:(我知道它是未定义的行为,但可能在许多实现中按预期工作)
void fn(void)
{
/* write something after this comment so that the program output is 10 */
int a[1] = {0};
int j = 0;
while(a[j] != 5) ++j; /* Search stack until you find 5 */
a[j] …
Run Code Online (Sandbox Code Playgroud) for (;;);
C中的未定义行为是无限循环吗?(它适用于C++,但我不知道C.)
While investigating a dubious claim, I wrote this little test program noway.c
int proveit()
{
unsigned int n = 0;
while (1) n++;
return 0;
}
int main()
{
proveit();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Testing this, I get:
$ clang -O noway.c
$ ./a.out
zsh: illegal hardware instruction ./a.out
Run Code Online (Sandbox Code Playgroud)
Wat.
如果我不进行优化就进行编译,则它会按预期挂起。我看了看程序集,没有所有的花哨main
功能,函数看起来像这样:
_main: ## @main
pushq %rbp
movq %rsp, %rbp
ud2
Run Code Online (Sandbox Code Playgroud)
其中ud2
显然是一个指令专门为未定义行为。前面提到的可疑声明“永不返回的函数是UB”得到了增强。我仍然很难相信。真!?您不能安全地编写自旋循环吗?
所以我想我的问题是:
相关资料
$ clang --version
Apple clang version 11.0.0 (clang-1100.0.20.17)
Target: x86_64-apple-darwin18.6.0 …
Run Code Online (Sandbox Code Playgroud) 当我编译并运行此代码时
#include <stdio.h>
int collatz(int a) {
return a == 1 ? 1 : collatz((a%2) ? 3*a+1 : a/2);
}
int main() {
for (int a = 0; a < 10; ++a)
printf("%d: %d\n", a, collatz(a));
}
Run Code Online (Sandbox Code Playgroud)
在clang
(10.0.0-4ubuntu1)中它崩溃了,但是当我添加-O3
它时它运行良好。(当将此作为 C 或 C++ 代码传递给clang
.)
我知道它在没有优化标志的情况下崩溃,因为collatz
with的调用0
将导致无限递归。但是,随着-O3
它返回0: 1
,这是错误的。这是优化中的(已知)错误还是我遗漏了什么?
(gcc
/ g++
(9.3.0) 不优化也崩溃,编译挂起-O3
。)
编辑:只是补充一点,我不是在寻找程序挂起/失败的原因(这很清楚),而是为什么编译器显然可以自由返回一些值而程序不应返回(但进入无限循环)。
编辑 2:为什么我没有选择一个更小的例子?嗯,在这里,这给出了相同的行为
int divide(int i) {
return i == 1 ? …
Run Code Online (Sandbox Code Playgroud) 在这个简单的示例中,我对使用有符号循环计数器和无符号循环计数器之间的差异感到非常惊讶:
double const* a;
__assume_aligned(a, 64);
double s = 0.0;
//for ( unsigned int i = 0; i < 1024*1024; i++ )
for ( int i = 0; i < 1024*1024; i++ )
{
s += a[i];
}
Run Code Online (Sandbox Code Playgroud)
在签名的情况下,生成了 icc 19.0.0(我显示了循环的展开部分):
..B1.2:
vaddpd zmm7, zmm7, ZMMWORD PTR [rdi+rax*8]
vaddpd zmm6, zmm6, ZMMWORD PTR [64+rdi+rax*8]
vaddpd zmm5, zmm5, ZMMWORD PTR [128+rdi+rax*8]
vaddpd zmm4, zmm4, ZMMWORD PTR [192+rdi+rax*8]
vaddpd zmm3, zmm3, ZMMWORD PTR [256+rdi+rax*8]
vaddpd zmm2, zmm2, ZMMWORD PTR [320+rdi+rax*8]
vaddpd zmm1, …
Run Code Online (Sandbox Code Playgroud) optimization assembly icc compiler-optimization unsigned-integer