memcpy中使用AVX指令集可以让cpu操作内存的粒度扩大

memcpy和自定义的my_memcpy函数,为什么cpu对内存的读写行为不一致

memcpy 是标准库函数,自定义的 my_memcpy 函数通常是按字节逐次复制的简单实现,二者在 CPU 对内存的读写行为上可能不一致,以下是详细分析:

1. 实现方式不同
自定义 my_memcpy:
通常采用按字节逐个复制的方式,示例代码如下:

void* my_memcpy(void* dest, const void* src, size_t n) {
    char* d = (char*)dest;
    const char* s = (const char*)src;
    for (size_t i = 0; i < n; i++) {
        d[i] = s[i];
    }
    return dest;
}

这种方式每次只读写一个字节的数据,CPU 频繁地进行内存访问,会产生较多的内存读写操作,导致读写次数较多。
标准库 memcpy:
标准库中的 memcpy 函数为了提高性能,会采用更高效的实现策略。它会根据不同的硬件平台和数据大小,采用字(通常是 4 字节或者 8 字节)甚至更大块的内存复制方式。例如,在 64 位系统中,可能会一次复制 8 字节的数据,减少 CPU 访问内存的次数,从而提高复制效率。
2. 优化手段不同
自定义 my_memcpy:
一般没有经过复杂的优化,代码逻辑简单直接。它没有考虑到硬件特性,例如缓存、内存对齐等因素,可能会导致频繁的缓存缺失,影响性能。
标准库 memcpy:
编译器和标准库开发者会针对不同的 CPU 架构进行优化。例如:
内存对齐优化:如果源地址和目标地址都是内存对齐的,memcpy 可以利用 CPU 的特性,以更高效的方式进行数据复制。例如,在某些架构中,对齐的内存访问比非对齐的内存访问速度更快。
使用特殊指令:一些现代 CPU 提供了专门用于内存复制的指令,如 SSE(Streaming SIMD Extensions)、AVX(Advanced Vector Extensions)等。标准库的 memcpy 函数可能会使用这些指令来并行地复制多个数据元素,从而提高复制速度。
3. 缓存行为不同
自定义 my_memcpy:
由于是按字节逐个复制,可能会导致频繁的缓存缺失。每次读取一个字节的数据时,CPU 可能需要从主存中获取,而不是从缓存中获取,这会增加内存访问的延迟。
标准库 memcpy:
采用大块数据复制的方式,更有利于利用 CPU 的缓存机制。当一次复制多个字节的数据时,这些数据可能会被一次性加载到缓存中,后续的访问可以直接从缓存中获取,减少了从主存中读取数据的次数,提高了访问速度。

**可以在自定义的 memcpy 函数中使用 AVX 指令集。**AVX(Advanced Vector Extensions)指令集提供了更强大的单指令多数据(SIMD)能力,能让程序在单个指令周期内处理多个数据元素,从而显著提升内存复制操作的效率。以下为你详细介绍如何在自定义 memcpy 函数里运用 AVX 指令集。
前提条件
编译器支持:要保证你的编译器支持 AVX 指令集。像 GCC 和 Clang 这样的常见编译器,可通过 -mavx 编译选项来启用 AVX 支持。
硬件支持:运行程序的 CPU 必须支持 AVX 指令集。

#include <immintrin.h>
#include <stddef.h>

void* my_memcpy_avx(void* dest, const void* src, size_t n) {
    size_t i;
    __m256i *d = (__m256i *)dest;
    const __m256i *s = (const __m256i *)src;

    // 以 32 字节(256 位)为单位进行复制
    for (i = 0; i + 31 < n; i += 32) {
        __m256i data = _mm256_loadu_si256(s);
        _mm256_storeu_si256(d, data);
        d++;
        s++;
    }

    // 处理剩余的字节
    char *char_d = (char *)d;
    const char *char_s = (const char *)s;
    for (; i < n; i++) {
        *char_d = *char_s;
        char_d++;
        char_s++;
    }

    return dest;
}

代码解释
头文件包含:
#include <immintrin.h>:此头文件包含了 AVX 指令集的内在函数,这些函数可用于调用 AVX 指令。
数据类型转换:
__m256i 是 AVX 指令集里用于处理 256 位整数向量的数据类型。把 dest 和 src 指针转换为 __m256i * 类型,这样就能以 32 字节为单位进行操作。
以 32 字节为单位复制:
_mm256_loadu_si256:从源地址加载 32 字节的数据到 __m256i 寄存器。
_mm256_storeu_si256:把 __m256i 寄存器中的 32 字节数据存储到目标地址。
通过循环不断复制 32 字节的数据,直至剩余数据不足 32 字节。
处理剩余数据:
当剩余数据不足 32 字节时,将指针转换为 char * 类型,按字节逐个复制剩余的数据。
编译和使用
若要编译包含 AVX 指令的代码,可使用以下命令:

gcc -mavx your_file.c -o your_program

注意事项
内存对齐:上述代码使用的是未对齐加载和存储指令(_mm256_loadu_si256 和 _mm256_storeu_si256),即便地址未对齐也能正常工作,但性能可能会受到一定影响。若地址是对齐的,可使用对齐加载和存储指令(_mm256_load_si256 和 _mm256_store_si256)来进一步提升性能。
硬件兼容性:确保运行程序的 CPU 支持 AVX 指令集,否则程序可能会崩溃或产生未定义行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值