我的x64多线程应用程序的行为非常奇怪。 调试模式下的执行时间比发布模式下的执行时间快。
我打破了问题,发现了问题: 调试模式优化(!注意优化已关闭!)memmpy memmove,其速度更快。释放模式仍然使用memcpy(!note optimition on)。
此问题在发布模式下减慢了我的多线程应用程序。 :(
有人有任何想法吗?
#include <time.h>
#include <iostream>
#define T_SIZE 1024*1024*2
int main()
{
clock_t start, end;
char data[T_SIZE];
char store[100][T_SIZE];
start = clock();
for (int i = 0; i < 4000; i++) {
memcpy(store[i % 100], data, T_SIZE);
}
// Debug > Release Time 1040 < 1620
printf("memcpy: %d\n", clock() - start);
start = clock();
for (int i = 0; i < 4000; i++) {
memmove(store[i % 100], data, T_SIZE);
}
// Debug > Release Time 1040 > 923
printf("memmove: %d\n", clock() - start);
}
答案 0 :(得分:6)
我们这里的内容实际上比memcpy
与memmove
更为奇怪。内在优化的一个案例实际上减慢了事情。问题源于VS2013如此内联memcopy:
; 73 : memcpy(store[i % 100], data, sizeof(data));
mov eax, 1374389535 ; 51eb851fH
mul esi
shr edx, 5
imul eax, edx, 100 ; 00000064H
mov ecx, esi
sub ecx, eax
movsxd rcx, ecx
shl rcx, 21
add rcx, r14
mov rdx, r13
mov r8d, 16384 ; 00004000H
npad 12
$LL413@wmain:
movups xmm0, XMMWORD PTR [rdx]
movups XMMWORD PTR [rcx], xmm0
movups xmm1, XMMWORD PTR [rdx+16]
movups XMMWORD PTR [rcx+16], xmm1
movups xmm0, XMMWORD PTR [rdx+32]
movups XMMWORD PTR [rcx+32], xmm0
movups xmm1, XMMWORD PTR [rdx+48]
movups XMMWORD PTR [rcx+48], xmm1
movups xmm0, XMMWORD PTR [rdx+64]
movups XMMWORD PTR [rcx+64], xmm0
movups xmm1, XMMWORD PTR [rdx+80]
movups XMMWORD PTR [rcx+80], xmm1
movups xmm0, XMMWORD PTR [rdx+96]
movups XMMWORD PTR [rcx+96], xmm0
lea rcx, QWORD PTR [rcx+128]
movups xmm1, XMMWORD PTR [rdx+112]
movups XMMWORD PTR [rcx-16], xmm1
lea rdx, QWORD PTR [rdx+128]
dec r8
jne SHORT $LL413@wmain
这个问题是我们正在进行未对齐的SSE加载和存储,这实际上比仅使用标准C代码要慢。我通过从visual studio中包含的源代码中获取CRT实现并进行my_memcpy
作为确保缓存在所有这些过程中都很温暖的一种方法,我已将所有data
预先初始化,但结果告诉我们:
热身时间为43毫秒 my_memcpy上涨了862ms
memmove up花了676ms
memcpy up花了1329ms
为什么memmove
更快?因为它不会尝试事先优化,因为它必须假设数据可以重叠。
对于那些好奇的人来说,这是我的全部代码:
#include <cstdlib>
#include <cstring>
#include <chrono>
#include <iostream>
#include <random>
#include <functional>
#include <limits>
namespace {
const auto t_size = 1024ULL * 1024ULL * 2ULL;
__declspec(align(16 )) char data[t_size];
__declspec(align(16 )) char store[100][t_size];
void * __cdecl my_memcpy(
void * dst,
const void * src,
size_t count
)
{
void * ret = dst;
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
}
int wmain(int argc, wchar_t* argv[])
{
using namespace std::chrono;
std::mt19937 rd{ std::random_device()() };
std::uniform_int_distribution<short> dist(std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
auto random = std::bind(dist, rd);
auto start = steady_clock::now();
// warms up the cache and initializes
for (int i = 0; i < t_size; ++i)
data[i] = static_cast<char>(random());
auto stop = steady_clock::now();
std::cout << "Warm up took " << duration_cast<milliseconds>(stop - start).count() << "ms\n";
start = steady_clock::now();
for (int i = 0; i < 4000; ++i)
my_memcpy(store[i % 100], data, sizeof(data));
stop = steady_clock::now();
std::cout << "my_memcpy took " << duration_cast<milliseconds>(stop - start).count() << "ms\n";
start = steady_clock::now();
for (int i = 0; i < 4000; ++i)
memmove(store[i % 100], data, sizeof(data));
stop = steady_clock::now();
std::cout << "memmove took " << duration_cast<milliseconds>(stop - start).count() << "ms\n";
start = steady_clock::now();
for (int i = 0; i < 4000; ++i)
memcpy(store[i % 100], data, sizeof(data));
stop = steady_clock::now();
std::cout << "memcpy took " << duration_cast<milliseconds>(stop - start).count() << "ms\n";
std::cin.ignore();
return 0;
}
调试时我发现编译器确实检测到我从CRT复制的代码是memcpy
,但它将它链接到CRT本身使用{{1}的非内在版本而不是上面的大规模SSE循环。似乎问题仅在于内在版本。
评论中的Per Z boson似乎这完全取决于架构。在我的CPU rep movs
上更快,但在较旧的CPU上,SSE或AVX实现可能更快。这是Intel Optimization Manual。对于未对齐的数据,rep movsb
可能会对旧硬件造成高达25%的损失。然而,也就是说,似乎绝大多数案例和架构rep movsb
平均会超过SSE或AVX实施。
答案 1 :(得分:0)
想法:致电memmove
,因为它对您来说最快。