我正在编写C代码并为PowerPC架构编译它.该C代码包含浮点变量常量,我希望将其放在该.text部分中,而不是.rodata功能代码是自包含的.
这个问题在于PowerPC,将浮点值移动到浮点寄存器的唯一方法是从内存加载它.这是一个指令集限制.
为了说服GCC帮助我,我尝试将浮标声明为static const.没有不同.使用指针,结果相同.使用__attribute__((section(".text")))该功能,相同的结果和每个浮点变量分别:
error: myFloatConstant causes a section type conflict with myFunction
Run Code Online (Sandbox Code Playgroud)
我也试过通过#pragma GCC push_options
#pragma GCC optimize("O0")和禁用优化#pragma GCC pop_options.再假装我有一个unsigned int工作:
unsigned int *myFloatConstant = (unsigned int *) (0x11000018);
*myFloatConstant = 0x4C000000;
Run Code Online (Sandbox Code Playgroud)
使用浮点数:
float theActualFloat = *(float *) myFloatConstant;
Run Code Online (Sandbox Code Playgroud)
我仍然想保留,-O3但它再次使用,.rodata所以一个潜在的答案将包括哪个优化标志导致浮动被放置,.rodata因为从这开始-O1发生?
最好的情况是我可以在代码中"正常"使用浮点数加上最大优化,但它们根本就不会被放置.rodata.
我想象 …
在x86上,有人可以确认,零位移跳跃(即不会改变CS或IP值的跳转)是否会清除指令预取队列?
Haswell及更早版本的ADC通常为2 uops,有2个周期延迟,因为Intel uops传统上只能有2个输入(https://agner.org/optimize/).在Haswell为FMA引入3输入微指令和某些情况下的索引寻址模式的微融合之后,Broadwell/Skylake及其后来都有单uop ADC/SBB/CMOV .
(但不适用于adc al, imm8短格式编码,或其他al/ax/eax/rax,imm8/16/32/32短格式,没有ModRM.我的答案中有更详细的说明.)
但是adc,即时0是特殊的Haswell解码为只有一个uop. @BeeOnRope测试了这个,并在他的uarch-bench中包含了对这个性能怪癖的检查:https://github.com/travisdowns/uarch-bench.从输出样本CI一个的Haswell服务器上示出之间的差adc reg,0和adc reg,1或adc reg,zeroed-reg.
(对于SBB也是如此.就我所见,在任何CPU上具有相同立即数的等效编码,ADC和SBB性能之间从来没有任何差别.)
这个优化何时adc bl,0推出?
我测试了Core 2 1,发现imm=0延迟是2个周期,相同adc eax,0.同时,也是循环计数是与吞吐量测试一些变化相同的adc eax,3对比0,所以第一代的Core 2(Conroe处理器/ Merom处理器)并没有这样做优化.
回答这个问题的最简单方法可能是在Sandybridge系统上使用我的测试程序,看看是否3比它快adc eax,0.但基于可靠文档的答案也可以.
(顺便说一句,如果有人可以访问Sandybridge上的perf计数器,你还可以通过运行@ BeeOnRope的测试代码来清除在执行uop计数不是处理器宽度倍数的循环时性能降低的谜团.或者是性能我在不再工作的SnB上观察到的只是因为未分层与正常的uops有什么不同?)
脚注1:我在运行Linux的Core 2 E6600(Conroe/Merom)上使用了这个测试程序.
;; NASM / YASM
;; assemble / link this …Run Code Online (Sandbox Code Playgroud) 我正在尝试针对特定的 Kaby Lake CPU (i5-7300HQ) 优化以下子例程,理想情况下,与原始形式相比,代码速度至少快 10 倍。该代码在 16 位实模式下作为软盘式引导加载程序运行。它在屏幕上显示一个十位数的十进制计数器,从 0 - 9999999999 计数然后停止。
我查看了 Agner 的微体系结构和汇编优化指南、 指令性能表和英特尔的优化参考手册。
到目前为止,我能够做的唯一明智的优化是将loop指令交换为dec + jnz,在此处进行解释。
另一种可能的优化可能是交换lodsbfor mov + dec,但我发现的关于它的信息一直存在冲突,有些人说它有一点帮助,而另一些人则认为它实际上可能会损害现代 CPU 的性能。
我还尝试切换到 32 位模式并将整个计数器保留在一个未使用的寄存器对中以消除任何内存访问,但在读入一点后我意识到这十位将立即被缓存,并且 L1 缓存之间的延迟差异和寄存器只有大约三倍,所以绝对不值得以这种格式使用计数器的额外开销。
(编者注:add reg延迟为 1 个周期,add [mem]延迟约为 6 个周期,包括 5 个周期的存储转发延迟。如果[mem]像视频 RAM 那样不可缓存,则更糟。)
org 7c00h
pos equ 2*(2*80-2) ;address on screen
;init
cli
mov ax,3
int 10h
mov …Run Code Online (Sandbox Code Playgroud) 以下代码片段仅使用一条RET指令创建一个函数(fun).循环重复调用该函数并在返回后覆盖RET指令的内容.
#include <sys/mman.h>
#include<stdlib.h>
#include<unistd.h>
#include <string.h>
typedef void (*foo)();
#define RET (0xC3)
int main(){
// Allocate an executable page
char * ins = (char *) mmap(0, 4096, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, 0, 0);
// Just write a RET instruction
*ins = RET;
// make fun point to the function with just RET instruction
foo fun = (foo)(ins);
// Repeat 0xfffffff times
for(long i = 0; i < 0xfffffff; i++){
fun();
*ins = RET;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
X86 Broadwell机器上的Linux perf具有以下icache和iTLB统计信息: …
我认为热补丁假设用 2 字节跳转覆盖任何 2 或更多字节长的指令对于并发执行相同的代码是安全的。
因此取指令被假定为原子的。
考虑到使用前缀可以有超过 8 字节的指令,并且它可以跨越任何对齐的边界,它确实是原子的吗?(或者热补丁是否依赖于函数开始的 16 字节对齐?如果是这样,那么大小超过 8 字节又是什么?)
上下文:LLVM 在https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/interception/interception_win.cpp中拦截了 API 函数。这至少用于 Address Sanitizer,也许也用于其他用途。它将 HotPatch 实现为第三种方法(第 61 行):
// 3) HotPatch
//
// The HotPatch hooking is assuming the presence of an header with padding
// and a first instruction with at least 2-bytes.
//
// The reason to enforce the 2-bytes limitation is to provide the minimal
// space to encode a short jump. HotPatch technique is only rewriting one …Run Code Online (Sandbox Code Playgroud) 我知道之前有人问这样的类似问题,但是有太多相互矛盾的信息,我真的想尝试一劳永逸地清理它.我将通过明确区分指令集架构(ISA)和实际硬件实现来尝试这样做.首先我尝试澄清:
1.)目前有intel64和amd64 CPU(除其他外,但这些都是重点)
2.)鉴于ISA是一个或多个CPU指令的二进制表示,这意味着ISA与其实际硬件实现完全分离.
我的问题:
intel 64和amd64 CPU之间的区别是否与不同的或扩展的x86-64 ISA有关?或者x86-64 ISA的不同硬件实现?或两者?
以下代码在调试模式下工作正常,因为如果没有设置Bit,_BitScanReverse64被定义为返回0.引用MSDN :(返回值为)"如果设置了索引则为非零,如果未找到设置位,则为0."
如果我在发布模式下编译此代码它仍然有效,但如果我启用编译器优化,例如\ O1或\ O2,则索引不为零且assert()失败.
#include <iostream>
#include <cassert>
using namespace std;
int main()
{
unsigned long index = 0;
_BitScanReverse64(&index, 0x0ull);
cout << index << endl;
assert(index == 0);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是预期的行为吗?我正在使用Visual Studio Community 2015,版本14.0.25431.01更新3.(我离开了cout,因此在优化期间不会删除变量索引).还有一个有效的解决方法或我不应该直接使用此编译器内在?
我不太想知道在每个平台上开发代码时程序员习惯于什么或他觉得更容易做的事情等方面的“小字”差异。我也对详细的物理差异不感兴趣核心(我不介意提及它们,如果它适合您的叙述,我只是不想专注于上述内容)
我只是在寻找为什么像 x86 这样的 CISC 架构优于 RISC 架构,或者不是?
我的意思是,如果您可以在降低复杂性 (RISC) 的情况下做所有事情,那么为什么要成为“复杂”(CISC)
有什么是 x86 可以做而 ARM 做不到的吗?如果什么都没有,那么为什么我们(历史上)费心开发 CISC 而没有专注于 RISC?
今天,ARM 似乎可以完成英特尔计算机所做的一切,他们甚至还有面向服务器的设计……
它让我叔叔感到震惊。。
在x86-64平台上,CLFLUSH汇编指令允许刷新对应于给定地址的缓存行.相反,冲洗与特定地址的缓存,会有一种通过使其充满了虚拟的内容,以刷新整个高速缓存(或者相关程序的缓存中执行,或整个高速缓存),例如(或任何我不会意识到的其他方法):
以下函数的内容是什么:(无论编译器优化如何,该函数都应该工作)?
void flush_cache()
{
// Contents
}
Run Code Online (Sandbox Code Playgroud) x86 ×7
assembly ×5
intel ×3
optimization ×3
x86-64 ×3
c++ ×2
performance ×2
amd ×1
arm ×1
atomic ×1
bootloader ×1
c ×1
cpu ×1
cpu-cache ×1
gcc ×1
hotpatching ×1
intrinsics ×1
memory ×1
perf ×1
powerpc ×1