整数除法/和模%运算通常在编程中一起使用,有时甚至在相同的操作数和后续行中使用.例如,下面的C函数是一个简单的函数,它将/2个数字的结果与它们的结果相加%,就是这样:
int sum2digits(int x, int base) {
int n, m;
n = x / base;
m = x % base;
return n + m;
}
Run Code Online (Sandbox Code Playgroud)
据我所知,双方/并%通过(在x86)的同一台机器的指令执行.说,如果为整数除法(执行机器指令div或idiv两个数字的),a和b,然后之后的值a / b将被存储在寄存器EAX,其余a % b在EDX.
我想知道编译器是否利用了这种质量并看了一下汇编代码.事实证明,使用gcc进行正常编译并不能优化:
push %rbp
mov %rsp,%rbp
mov %edi,-0x14(%rbp)
mov %esi,-0x18(%rbp)
mov -0x14(%rbp),%eax
mov %eax,%edx
sar $0x1f,%edx
idivl -0x18(%rbp)
mov %eax,-0x8(%rbp)
mov -0x14(%rbp),%eax
mov %eax,%edx
sar $0x1f,%edx
idivl …Run Code Online (Sandbox Code Playgroud) 出于好奇,我想知道gcc是否可以通过某种方式不优化任何函数调用?
在生成的汇编代码中,printf函数由putchar代替。即使使用默认的-O0最小优化标志,也会发生这种情况。
#include <stdio.h>
int main(void) {
printf("a");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
(Godbolt显示GCC9正在执行此操作,clang8使其保持不变。)
我想知道变量的初始化方式:
#include <stdio.h>
int main( void )
{
int ghosts[3];
for(int i =0 ; i < 3 ; i++)
printf("%d\n",ghosts[i]);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这给我带来了随机值,例如 -12 2631 131 ..它们来自哪里?
例如,x86-64 Linux 上的 GCC:https ://godbolt.org/z/MooEE3ncc
我有一个猜测来回答我的问题,无论如何它都可能是错误的:
内存的寄存器在“清空”后获得 0 和 1 之间的随机电压,这些值“四舍五入”为 0 或 1,并且这些随机值取决于在某事上?!也许寄存器的制作方式?也许内存的容量会以某种方式发挥作用?甚至可能是温度?!!
最近我一直在努力学习逆向工程.因此,我一直在研究很多汇编代码.我对以下内容感到困惑:
movq %rax,0xf8(%rbp)
movq 0xf8(%rbp),%rax
Run Code Online (Sandbox Code Playgroud)
我已经好几次见过这个了.这不是多余的吗?为什么编译器会这样做?我正在看的二进制文件是用gcc编译的.
我正在做一个家庭作业问题,要求我找出运行我在C中写的短程序时执行的机器代码指令的数量.
问题是我可以使用我想要的任何工具来解决它,但我对C很新,并且很少知道如何解决这个问题.
我需要哪些类型的工具来解决这个问题?
只有1种情况__builtin_clz给出错误的答案。我很好奇是什么导致了这种行为。
当我使用文字值0时,我总是得到32的期望值。但是0作为变量将产生31。为什么存储值0的方法很重要?
我上过架构课程,但不了解差异化的程序集。看起来当给定字面值0时,即使不进行优化,该汇编总会以某种方式始终具有32个硬编码的正确答案。使用-march = native时,用于计算前导零的方法也不同。
这篇文章关于模拟__builtin_clz与_BitScanReverse和行bsrl %eax, %eax似乎意味着位扫描反向不起作用0。
+-------------------+-------------+--------------+
| Compile | literal.cpp | variable.cpp |
+-------------------+-------------+--------------+
| g++ | 32 | 31 |
| g++ -O | 32 | 32 |
| g++ -march=native | 32 | 32 |
+-------------------+-------------+--------------+
Run Code Online (Sandbox Code Playgroud)
#include <iostream>
int main(){
int i = 0;
std::cout << __builtin_clz(0) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
#include <iostream>
int main(){
int i = 0;
std::cout << __builtin_clz(i) << std::endl;
} …Run Code Online (Sandbox Code Playgroud) 正如我从测试用例中看到的:https : //godbolt.org/z/K477q1
生成的程序集加载/存储原子放松与普通变量相同:ldr 和 str
那么,松弛原子变量和普通变量之间有什么区别吗?
我需要找到比最后一个滚动最大峰值低 X 或更多 % 的值的索引。
峰值是一个数组 ( highs) 中元素的滚动最大值,而值位于另一个数组 ( lows) 中。数组具有相等的长度,并且值保证 <= peaks 数组中的对应项,没有 0、NAN 或无穷大元素。since保证小于till。
迭代实现很简单:
inline
size_t trail_max_intern(double *highs,
double *lows,
double max,
double trail,
size_t since,
size_t till)
{
for (; since < till; ++since) {
if (max < highs[since]) {
max = highs[since];
}
if (lows[since] / max <= trail) {
break;
}
}
return since;
}
size_t trail_max_iter(double *highs, double *lows, double trail, size_t since, size_t till)
{ …Run Code Online (Sandbox Code Playgroud) 我编写了两个获取数组总和的函数,第一个是用 C++ 编写的,另一个是用内联汇编 (x86-64) 编写的,我比较了这两个函数在我的设备上的性能。
如果在编译期间未启用-O标志,则使用内联汇编的函数几乎比 C++ 版本快 4-5 倍。
cpp time : 543070068 nanoseconds
cpp time : 547990578 nanoseconds
asm time : 185495494 nanoseconds
asm time : 188597476 nanoseconds
Run Code Online (Sandbox Code Playgroud)
如果-O标志设置为-O1,它们会产生相同的性能。
cpp time : 177510914 nanoseconds
cpp time : 178084988 nanoseconds
asm time : 179036546 nanoseconds
asm time : 181641378 nanoseconds
Run Code Online (Sandbox Code Playgroud)
但是,如果我尝试将-O标志设置为-O2或-O3,则使用内联汇编编写的函数会得到不寻常的2-3 位纳秒性能,该性能速度很快(至少对我来说,请耐心等待,因为我对汇编编程没有扎实的经验,所以我不知道它与用 C++ 编写的程序相比有多快或多慢。)
cpp time : 177522894 nanoseconds
cpp time : 183816275 nanoseconds …Run Code Online (Sandbox Code Playgroud)我试图了解什么优化过程会导致以下代码在使用 -O3 优化标志编译时产生无限循环。为了解决这个问题,我知道问题的真正根本原因是这个非空函数中缺少返回,我在嵌入式系统上实现此代码的过程中发生了这种有趣的行为,并且没有但添加了返回值,因为当时我没有使用返回值。
我的问题更多是关于优化过程以及它正在做的事情在其他情况下如何提供帮助/“优化”逻辑是什么样的。
对于更多上下文,我在 ubuntu (c++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0) 中使用 c++ 编译器以及附带的 aarch64-linux-gnu-g++ 编译器时看到了这种行为Xilinx Vitis 2020.2(当然也可以在各自的平台上运行)。
最小可重现示例(我迄今为止创建的):
#include <iostream>
int broken_for_loop(){
for (int i = 0; i < 10000; i+= 1000){
std::cout << i << std::endl;
}
}
int main(int argc, char const *argv[]){
broken_for_loop();
}
Run Code Online (Sandbox Code Playgroud)
当使用c++ ./broken_loop_test.cpp -o test_local -O3ARM 或同等版本进行编译时。循环的输出是无限的,我已经运行它直到 32b int 结束。如果没有优化,它会按我的预期工作。如果我简单地return 0在 for 循环之后,它也适用于优化。
我天真的怀疑是,因为循环外部没有返回,编译器期望我从循环内部返回或突破,因此删除了测试循环条件的检查/分支,但我想知道我会做什么可以研究一下以获得有关这个特定主题的更多信息(以及一般的优化,自从我上一门编译器设计课程以来已经有一段时间了),而且我对 ASM 不太熟悉,无法自信地识别那里的问题。
任何帮助将不胜感激,谢谢!
因为本节是必需的,所以我会注意到我已经尝试声明volatile i和使用不同类型的整数以及转换常量值并在循环中执行更多/更少操作。没有 return 语句,所有这些都会导致相同的行为。
c++ compiler-errors infinite-loop compiler-optimization undefined-behavior
c ×5
assembly ×4
c++ ×4
gcc ×4
x86 ×3
optimization ×2
atomic ×1
avx2 ×1
c++11 ×1
intrinsics ×1
isa ×1
performance ×1
profiling ×1
simd ×1
stdatomic ×1