今天,我发现在添加了一些不相关的代码后,示例代码的速度降低了50%。调试后,我发现问题出在循环对齐中。根据循环代码的位置,有不同的执行时间,例如:
我以前没想到代码对齐会产生如此大的影响。我认为我的编译器足够聪明,可以正确对齐代码。
到底是什么导致了执行时间的如此大的差异?(我想是一些处理器架构细节)。
我用Visual Studio 2019在发布模式下编译的测试程序,并在Windows 10上运行。我在两个处理器上检查了该程序:i7-8700k(以上结果)和intel i5-3570k,但那里不存在问题,执行时间始终在1250us左右。我也尝试过用clang编译程序,但用clang的结果总是约1500us(在i7-8700k上)。
我的测试程序:
#include <chrono>
#include <iostream>
#include <intrin.h>
using namespace std;
template<int N>
__forceinline void noops()
{
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
noops<N - 1>();
}
template<>
__forceinline void noops<0>(){}
template<int OFFSET>
__declspec(noinline) void SumHorizontalLine(const unsigned char* __restrict src, int width, int a, unsigned short* __restrict dst)
{
unsigned short sum = 0;
const unsigned char* srcP1 = src - a - 1;
const unsigned char* srcP2 = src + a;
//some dummy loop,just a few iterations
for (int i = 0; i < a; ++i)
dst[i] = src[i] / (double)dst[i];
noops<OFFSET>();
//the important loop
for (int x = a + 1; x < width - a; x++)
{
unsigned char v1 = srcP1[x];
unsigned char v2 = srcP2[x];
sum -= v1;
sum += v2;
dst[x] = sum;
}
}
template<int OFFSET>
void RunTest(unsigned char* __restrict src, int width, int a, unsigned short* __restrict dst)
{
double minTime = 99999999;
for(int i = 0; i < 20; ++i)
{
auto start = chrono::steady_clock::now();
for (int i = 0; i < 1024; ++i)
{
SumHorizontalLine<OFFSET>(src, width, a, dst);
}
auto end = chrono::steady_clock::now();
auto us = chrono::duration_cast<chrono::microseconds>(end - start).count();
if (us < minTime)
{
minTime = us;
}
}
cout << OFFSET << " : " << minTime << " us" << endl;
}
int main()
{
const int width = 2048;
const int x = 3;
unsigned char* src = new unsigned char[width * 5];
unsigned short* dst = new unsigned short[width];
memset(src, 0, sizeof(unsigned char) * width);
memset(dst, 0, sizeof(unsigned short) * width);
while(true)
RunTest<1>(src, width, x, dst);
}
要验证不同的对齐方式,只需重新编译程序并更改RunTest
为OFFSET=1的循环生成的汇编代码(对于其他偏移量,只有npad的数量不同):
0007c 90 npad 1
0007d 90 npad 1
0007e 49 83 c1 08 add r9, 8
00082 90 npad 1
00083 90 npad 1
00084 90 npad 1
00085 90 npad 1
00086 90 npad 1
00087 90 npad 1
00088 90 npad 1
00089 90 npad 1
0008a 90 npad 1
0008b 90 npad 1
0008c 90 npad 1
0008d 90 npad 1
0008e 90 npad 1
0008f 90 npad 1
$LL15@SumHorizon:
; 25 :
; 26 : noops<OFFSET>();
; 27 :
; 28 : for (int x = a + 1; x < width - a; x++)
; 29 : {
; 30 : unsigned char v1 = srcP1[x];
; 31 : unsigned char v2 = srcP2[x];
; 32 : sum -= v1;
00090 0f b6 42 f9 movzx eax, BYTE PTR [rdx-7]
00094 4d 8d 49 02 lea r9, QWORD PTR [r9+2]
; 33 : sum += v2;
00098 0f b6 0a movzx ecx, BYTE PTR [rdx]
0009b 48 8d 52 01 lea rdx, QWORD PTR [rdx+1]
0009f 66 2b c8 sub cx, ax
000a2 66 44 03 c1 add r8w, cx
; 34 : dst[x] = sum;
000a6 66 45 89 41 fe mov WORD PTR [r9-2], r8w
000ab 49 83 ea 01 sub r10, 1
000af 75 df jne SHORT $LL15@SumHorizon
; 35 : }
; 36 :
; 37 : }
000b1 c3 ret 0
??$SumHorizontalLine@$00@@YAXPEIBEHHPEIAG@Z ENDP ; SumHorizont
我认为我的编译器足够聪明,可以正确地对齐代码。
正如你所说,编译器总是将事物对齐到16字节的倍数。这可能确实解释了对齐的直接影响。但是编译器的“智能”是有限制的。
除了对齐之外,代码放置也有间接的性能影响,因为缓存关联性。如果可以映射到该地址的少数缓存行的争用过多,性能就会受到影响。移动到争用较少的地址会使问题消失。
编译器可能足够聪明,可以处理缓存争用效应,但前提是启用概要文件引导优化。这些相互作用过于复杂,在合理的工作量内无法预测;通过实际运行程序,可以更容易地监视缓存冲突,而PGO就是这样做的。
在慢速情况下(即00007FF7750B1280和00007FF7750B12A0),jne指令跨越32字节边界。“跳转条件代码”(JCC)勘误表的缓解措施(https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf)防止在DSB中缓存此类指令。JCC勘误表仅适用于基于Skylake的CPU,这就是为什么这种影响不会在i5-3570k CPU上发生。
正如彼得·科尔德斯(PeterCordes)在一篇评论中指出的那样,最近的编译器有一些选项试图减轻这种影响。英特尔JCC勘误表-JCC真的应该单独对待吗?提及MSVC的QIntel jcc勘误表选项;另一个相关问题是,我如何减轻英特尔jcc勘误表对gcc的影响?
`多处理系统中,使用并发的方式来提高代码的效率时,你需要了解一下有哪些因素会影响并发的效率。即使已经使用多线程对关注进行分离,还需要确定是否会对性能造成负面影响。因为,在16核机器上应用的速度与单核机器相当时,用户是不会打死你的。 之后你会看到,在多线程代码中有很多因素会影响性能——对线程处理的数据做一些简单的改动(其他不变),都可能对性能产生戏剧性的效果。所以,多言无益,让我们来看一下这些因素吧
使用时,是否有需要考虑的性能影响? 我正在编写一个从目录检索文件的查询,这就是查询: 那么,在决定进行这样的转换时,是否应该考虑某种性能影响--还是只在处理大量文件时才考虑?这是一个可以忽略不计的转换吗?
问题内容: 我有这个JavaWeb应用程序,它可以从电子表格上传成千上万的数据,该电子表格是从上到下按行读取的。我用来在服务器端显示应用程序当前正在读取的行。 -我知道要创建一个日志文件。实际上,我正在创建一个日志文件,同时在服务器提示符下显示日志。 还有其他方法可以在提示上打印当前数据? 问题答案: 它可能会影响您的应用程序性能。大小会因您所运行的硬件类型和主机上的负载而异。 可以将其转化为性能
前言 HTTPS 在保护用户隐私,防止流量劫持方面发挥着非常关键的作用,但与此同时,HTTPS 也会降低用户访问速度,增加网站服务器的计算资源消耗。 本文主要介绍 https 对用户体验的影响。 HTTPS 对访问速度的影响 在介绍速度优化策略之前,先来看下 HTTPS 对速度有什么影响。影响主要来自两方面: 协议交互所增加的网络 RTT(round trip time)。 加解密相关的计算耗时。
我编写了所需的带有注释的接口和作为装饰器的抽象类。生成(mvn清理包)后,通过“默认”过程更新修饰的函数,得到的是参数和结果类型。我不知道,有什么问题。你能帮帮我吗? 环境:mapstruct版本1.4.2。lombok最终版本1.18.22(Spring boot 2.6.3)lombok mapstruct绑定:0.2.0 和 Mapper接口声明: 装饰师: } 以及生成的源:
问题内容: varchar列上的索引是否会使查询运行缓慢?我可以将其设为int。而且我不需要做LIKE%比较。 问题答案: varchar列上的索引是否会使查询运行缓慢? 不,不是的。 如果优化器决定使用索引,则查询将运行得更快。 该表上的s / s / s会变慢,但不太可能引起注意。 我不需要做LIKE%比较 请注意,使用: …将 不 使用索引,但以下内容将: 关键是在字符串的左侧使用通配符,这