Memcpy vs Memmove - 调试与发布
Memcpy vs Memmove - Debug vs Release
我的x64多线程应用程序的行为非常奇怪。调试模式下的执行时间比发布模式下快。
我分解了问题并发现了问题:调试方式优化(!请注意,优化已关闭!Memcpy到Memmove,它的速度更快。释放模式仍然使用memcpy(!注意优化已打开)。
此问题会减慢我的多线程应用程序在发布模式下的速度。 :(
有人知道吗?
#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: %dn", 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: %dn", clock() - start);
}
以下答案仅适用于VS2013
我们这里所拥有的实际上比memcpy
更奇怪。 memmove
.这是内在优化实际上减慢速度的情况。该问题源于VS2013内联内存拷贝的事实,如下所示:
; 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
内存移动上移耗时 676 毫秒
内存上升花了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() << "msn";
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() << "msn";
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() << "msn";
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() << "msn";
std::cin.ignore();
return 0;
}
更新
在调试时,我发现编译器确实检测到我从 CRT 复制的代码是memcpy
的,但它将其链接到 CRT 本身中的非内部版本,该版本使用 rep movs
而不是上面的大量 SSE 循环。似乎问题只出在内部版本上。
更新 2
根据评论中的Z玻色子,这似乎都非常依赖于架构。在我的CPU上,rep movsb
更快,但在较旧的CPU上,SSE或AVX实现有可能更快。这是根据英特尔优化手册。对于未对齐的数据,rep movsb
在旧硬件上可能会受到高达 25% 的损失。然而,话虽如此,对于绝大多数情况和架构,rep movsb
似乎平均会击败SSE或AVX实现。
想法:调用memmove
,因为它对您的情况来说是最快的。
- 正在VS调试器中监视映射条目
- 为什么我的 VS 代码调试器在我的C++代码周围弹跳?
- 如何在 WSL 上获取 VS 代码以调试我的C++代码?
- 带有 GDB 调试器的 VS 代码内存视图
- 使用 Windows 10 和 MINGW 编译器在 VS Code 上使用 gdb 调试器进行调试时出错
- 当 lib 已添加到其他依赖项时,如何在 VS 中调试未解析的外部符号错误
- 如何在Mac上调试VS code上的C++代码
- VTK和QT在VS中调试
- 在使用 vs 代码调试文件时,如何将文件传递到我的程序中
- C vs 2017设置全局变量 - 程序仅在调试中使用断点
- 使用 Microsoft VS 编写程序C++,但我在这里收到调试断言消息.它可以在 cpp.sh 上运行,repl.i
- VS Code C++程序在调试时不显示任何输出
- 使用无GDB的VS代码进行调试GNU
- 混合调试和发布库:Windows vs Linux,静态与共享
- VS 代码忽略 C++ 调试中的断点
- VS Code 上的简单 GDB C++调试失败,并显示 NullReferenceException
- VS 2015图形调试器给我源错误
- VS 代码C++:无法启动调试 - 程序路径丢失或无效
- 设定vs调试器的端
- 以下代码在 VS 2013 发布/调试中生成不同的结果