我已经使用Linux perf一段时间来进行应用程序分析.通常情况下,配置文件应用程序相当复杂,因此只要根据第一原则与您的预期没有任何严重差异,就可以简单地将报告的计数器值视为面值.
然而,最近,我已经描述了一些简单的64位汇编程序 - 足够的竞争,人们几乎可以计算各种计数器的预期值,而且似乎perf stat
是过度计算.
以下面的循环为例:
.loop:
nop
dec rax
nop
jne .loop
Run Code Online (Sandbox Code Playgroud)
这将简单地循环n
次数,其中n
是初始值rax
.循环的每次迭代都执行4条指令,因此您可以期望4 * n
执行指令,加上一些用于进程启动和终止的固定开销,以及n
在进入循环之前设置的一小段代码.
这是(典型)perf stat
输出n = 1,000,000,000
:
~/dev/perf-test$ perf stat ./perf-test-nop 1
Performance counter stats for './perf-test-nop 1':
301.795151 task-clock (msec) # 0.998 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
2 page-faults # 0.007 K/sec
1,003,144,430 cycles # 3.324 GHz
4,000,410,032 instructions …
Run Code Online (Sandbox Code Playgroud) 我已经了解了不同的缓存映射技术,如直接映射,关联映射和集合关联映射技术,还学习了权衡.但我很好奇现在在intel core i7或AMD处理器中使用了什么.以及这些技术是如何演变的.还有哪些事情需要改进?
我正在尝试使用SSE42和STTNI指令并得到奇怪的结果 - PcmpEstrM(使用显式长度字符串)比PcmpIstrM(隐式长度字符串)运行慢两倍.
两者都是"常春藤桥" - 奇怪的是他们有如此不同的"差异"(至少我看不出他们的规格有任何技术差异 - http://www.cpu-world.com/Compare_CPUs/Intel_AW8063801013511,Intel_CM8063701093302 /).
英特尔64和IA-32架构优化参考手册提到了PcmpEstrM和PcmpIstrM的相同吞吐量= 11和延迟= 3.因此,我预计两者的表现相似.
问:差异是我实际设计/预期的,还是我以错误的方式使用这些指令?
下面是我的虚拟测试场景(VS 2012).逻辑非常简单 - 扫描16MB文本以查找匹配字符.由于干草堆和针头串都没有包含零终结器 - 我希望E和I具有相似的性能.
PS:我尝试在intel的开发论坛上发布这个问题,但他们将其识别为垃圾邮件:(
#include "stdafx.h"
#include <windows.h>
#define BEGIN_TIMER(NAME) \
{ \
LARGE_INTEGER __freq; \
LARGE_INTEGER __t0; \
LARGE_INTEGER __t1; \
double __tms; \
const char* __tname = NAME; \
char __tbuf[0xff]; \
\
QueryPerformanceFrequency(&__freq); \
QueryPerformanceCounter(&__t0);
#define END_TIMER() \ …
Run Code Online (Sandbox Code Playgroud) 我遇到了使用PCLMULQDQ实现的快速CRC计算.我看到,那些人混合pxor
和xorps
说明很像下面的片段:
movdqa xmm10, [rk9]
movdqa xmm8, xmm0
pclmulqdq xmm0, xmm10, 0x11
pclmulqdq xmm8, xmm10, 0x0
pxor xmm7, xmm8
xorps xmm7, xmm0
movdqa xmm10, [rk11]
movdqa xmm8, xmm1
pclmulqdq xmm1, xmm10, 0x11
pclmulqdq xmm8, xmm10, 0x0
pxor xmm7, xmm8
xorps xmm7, xmm1
Run Code Online (Sandbox Code Playgroud)
这有什么实际的理由吗?性能提升?如果是,那么这下面是什么?或者它可能只是一种编码风格,有趣吗?
我熟悉数据对齐和性能,但对对齐代码相当陌生。我最近开始使用 NASM 在 x86-64 汇编中进行编程,并一直使用代码对齐来比较性能。据我所知,NASM 插入nop
指令来实现代码对齐。
这是我一直在 Ivy Bridge 系统上尝试的一个功能
void triad(float *x, float *y, float *z, int n, int repeat) {
float k = 3.14159f;
int(int r=0; r<repeat; r++) {
for(int i=0; i<n; i++) {
z[i] = x[i] + k*y[i];
}
}
}
Run Code Online (Sandbox Code Playgroud)
我为此使用的程序集如下。如果我不指定对齐方式,我的性能与峰值相比仅为 90% 左右。然而,当我将循环之前的代码以及两个内部循环对齐为 16 字节时,性能跃升至 96%。很明显,这种情况下的代码对齐会产生影响。
但这是最奇怪的部分。如果我将最里面的循环对齐到 32 字节,则该函数的性能没有任何差异,但是,在该函数的另一个版本中,在单独的对象文件中使用内部函数,我链接它的性能从 90% 跃升至 95%!
我做了一个对象转储(使用objdump -d -M intel
)的版本对齐到16字节(我将结果发布到这个问题的末尾)和32字节,它们是相同的!事实证明,在两个目标文件中,最里面的循环无论如何都与 32 字节对齐。但一定有一些区别。
我对每个目标文件进行了十六进制转储,目标文件中有一个字节不同。与 16 字节对齐的目标文件有一个带有 的字节0x10
,与 32 字节对齐的目标文件有一个带有 的字节0x20
。到底是怎么回事!为什么一个目标文件中的代码对齐会影响另一个目标文件中函数的性能?我如何知道将我的代码调整到的最佳值是多少?
我唯一的猜测是,当加载程序重新定位代码时,32 字节对齐的对象文件会使用内在函数影响其他对象文件。 …
在最近的高端Intel CPU上重新排序x64(x86-64)指令可以获得多少性能提升.在非常时间紧迫的情况下值得打扰吗?
我还想知道通过改变寄存器使用/使用额外的寄存器(如果空闲)来获得收益的可能性,以便在某些奇怪的情况下允许更长距离的代码移动?
如何在Assembly中乘以浮动?我在 ebx 中有一个值,并希望将其设为 0.65
mov eax, ebx
mov ecx, 0x0.65 ;; how do this?
mul ecx
mov ebx, eax
Run Code Online (Sandbox Code Playgroud) 我只是试图在x86汇编中进行非常快速的基于计算的程序,但我需要在调用程序之前推送累加器,计数器和数据寄存器.手动推动它们的速度更快:
push eax
push ecx
push edx
Run Code Online (Sandbox Code Playgroud)
或者只是使用,
pushad
Run Code Online (Sandbox Code Playgroud)
与弹出一样.谢谢
从开放资源中,我可以得出结论,微代码大约可以直接由CPU执行,并负责实现指令代码。维基百科还指出,指令代码的每次执行都会经历fetch-decode-execute指令周期。但是,我找不到任何参考资料来说明在此三个阶段中如何执行微代码。所以我的问题是,微代码执行与指令周期之间的关系是什么?微码在指令执行的获取,解码和执行阶段如何工作?
同样,这个stackoverflow的答案是说,在现代的Intel CPU中,即使最简单的指令(例如DIV
和)MOV
也将在执行之前以微码进行编译,因此,如果有人真的可以用此类CPU的示例进行解释,那将是最好的。
我正在阅读Jonathan Barlett撰写的" 从头开始编程 "一书,用于在Linux上学习i386程序集
我的目的是阅读一些用asm编写的项目的源代码,然后我遇到了这个LODSL
,从手册我可以知道它从哪里加载数据%esi
,然后增加地址大小
那么为什么人们不能只是movl
这样做呢?我没有考虑任何速度提升或任何其他问题?
我想测试按位运算是否真的比算术运算更快.我以为他们是.
我写了一个小的C程序来测试这个假设,令我惊讶的是,加法平均比按位AND运算少.这对我来说是令人惊讶的,我无法理解为什么会这样.
根据我所知的附加,来自较低有效位的进位应该被携带到下一位,因为结果也取决于进位.对我来说逻辑运算符比加法更慢是没有意义的.
我的鳕鱼在下面:
#include<stdio.h>
#include<time.h>
int main()
{
int x=10;
int y=25;
int z=x+y;
printf("Sum of x+y = %i", z);
time_t start = clock();
for(int i=0;i<100000;i++)z=x+y;
time_t stop = clock();
printf("\n\nArithmetic instructions take: %d",stop-start);
start = clock();
for(int i=0;i<100000;i++)z=x&y;
stop = clock();
printf("\n\nLogic instructions take: %d",stop-start);
}
Run Code Online (Sandbox Code Playgroud)
一些结果:
Arithmetic instructions take: 327
Logic instructions take: 360
Arithmetic instructions take: 271
Logic instructions take: 271
Arithmetic instructions take: 287
Logic instructions take: 294
Arithmetic instructions take: 279
Logic instructions take: …
Run Code Online (Sandbox Code Playgroud) c assembly instructions logical-operators integer-arithmetic
我想尽可能地优化我的函数,我做的一件事就是使用r8作为指针,因为这是指针在x64函数中被推入的寄存器.
但是推送RSI或RDI,将指针移动到它们并在循环中更快地使用它们?
例如,mov [RSI],DL;将编译为2个字节和:mov [r8],DL; 将编译为3个字节
所以,如果我做了100到200次循环,r8会因为要解码的额外字节而变慢吗?或推动RSI并移动指针消除任何可能的速度增加?显然push和mov会在循环外发生.