Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ memcpy胜过SIMD本质_C++_Performance_Arm_Simd_Intrinsics - Fatal编程技术网

C++ memcpy胜过SIMD本质

C++ memcpy胜过SIMD本质,c++,performance,arm,simd,intrinsics,C++,Performance,Arm,Simd,Intrinsics,当ARM设备上有霓虹灯矢量指令时,我一直在寻找复制各种数据量的快速方法 我做了一些基准测试,得到了一些有趣的结果。我在努力理解我在看什么 我有四个版本可以复制数据: 1.基线 逐元素复制: for (int i = 0; i < size; ++i) { copy[i] = orig[i]; } 4.正常memcpy 将memcpy与全部数据一起使用 memcpy(orig, copy4, size); 我使用2^16值进行的基准测试给出了一些令人惊讶的结果: 1. Basel

当ARM设备上有霓虹灯矢量指令时,我一直在寻找复制各种数据量的快速方法

我做了一些基准测试,得到了一些有趣的结果。我在努力理解我在看什么

我有四个版本可以复制数据:

1.基线 逐元素复制:

for (int i = 0; i < size; ++i)
{
    copy[i] = orig[i];
}
4.正常
memcpy
memcpy
与全部数据一起使用

memcpy(orig, copy4, size);
我使用
2^16
值进行的基准测试给出了一些令人惊讶的结果:

1. Baseline time = 3443[µs]
2. NEON time = 1682[µs]
3. memcpy (stepped) time = 1445[µs]
4. memcpy time = 81[µs]

霓虹灯时间的加速是可以预期的,但是更快的步进
memcpy
时间让我感到惊讶。而
4
的时间更是如此

为什么
memcpy
做得这么好?它在引擎盖下使用霓虹灯吗?还是有我不知道的高效内存拷贝指令

讨论了NEON与
memcpy()。然而,我觉得答案并没有充分探究为什么ARM
memcpy
实现运行得这么好

完整的代码清单如下:

#include <arm_neon.h>
#include <vector>
#include <cinttypes>

#include <iostream>
#include <cstdlib>
#include <chrono>
#include <cstring>

int main(int argc, char *argv[]) {

    int arr_size;
    if (argc==1)
    {
        std::cout << "Please enter an array size" << std::endl;
        exit(1);
    }

    int size =  atoi(argv[1]); // not very C++, sorry
    std::int32_t* orig = new std::int32_t[size];
    std::int32_t* copy = new std::int32_t[size];
    std::int32_t* copy2 = new std::int32_t[size];
    std::int32_t* copy3 = new std::int32_t[size];
    std::int32_t* copy4 = new std::int32_t[size];


    // Non-neon version
    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
    for (int i = 0; i < size; ++i)
    {
        copy[i] = orig[i];
    }
    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    std::cout << "Baseline time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;

    // NEON version
    begin = std::chrono::steady_clock::now();
    int32x4_t tmp;
    for (int i = 0; i < size; i += 4)
    {
        tmp = vld1q_s32(orig + i); // load 4 elements to tmp SIMD register
        vst1q_s32(&copy2[i], tmp); // copy 4 elements from tmp SIMD register
    }
    end = std::chrono::steady_clock::now();
    std::cout << "NEON time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;


    // Memcpy example
    begin = std::chrono::steady_clock::now();
    for (int i = 0; i < size; i+=4)
    {
        memcpy(orig+i, copy3+i, 4);
    }
    end = std::chrono::steady_clock::now();
    std::cout << "memcpy time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;


    // Memcpy example
    begin = std::chrono::steady_clock::now();
    memcpy(orig, copy4, size);
    end = std::chrono::steady_clock::now();
    std::cout << "memcpy time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;

    return 0;
}

#包括
#包括
#包括
#包括
#包括
#包括
#包括
int main(int argc,char*argv[]){
内部arr_大小;
如果(argc==1)
{

std::cout注意:此代码使用memcpy的方向错误。它应该是
memcpy(dest,src,num_bytes)

因为“正常的memcpy”测试是最后一次进行的,所以与其他测试相比,大规模的加速可以解释为。优化器发现
orig
在最后一次memcpy之后没有使用,所以它消除了memcpy


编写可靠基准测试的一个好方法是使用框架,并使用它们的
benchmark::DoNotOptimize(x)
函数防止死代码消除。

人们喜欢优化memcpy。我希望它会很快。普通
memcpy
调用比其他方法复制的数据少4倍。它以字节为单位计算大小,但传递的是4字节元素的数量。假设调用甚至可以编译-它指的是一个没有出现的名称
I
要在显示的代码中的任何位置声明。并且您传递参数的方式错误:第一个参数是副本的目标,第二个是源。此外,如果您正在优化程序,它可能正在执行死代码消除,因为它似乎没有实际执行任何操作。如果您没有优化它,所有数字都毫无价值nyway,因为库中的memcpy是绝对优化的。您可能希望使用一个基准测试库,在该库中,您可以使用
-Ofast
-O3
编译测试对象文件,并使用
-O0
的基准测试主函数编译所谓的“stepped
memcpy
”也只复制每个第4个元素,而且方向错误。
1. Baseline time = 3443[µs]
2. NEON time = 1682[µs]
3. memcpy (stepped) time = 1445[µs]
4. memcpy time = 81[µs]
#include <arm_neon.h>
#include <vector>
#include <cinttypes>

#include <iostream>
#include <cstdlib>
#include <chrono>
#include <cstring>

int main(int argc, char *argv[]) {

    int arr_size;
    if (argc==1)
    {
        std::cout << "Please enter an array size" << std::endl;
        exit(1);
    }

    int size =  atoi(argv[1]); // not very C++, sorry
    std::int32_t* orig = new std::int32_t[size];
    std::int32_t* copy = new std::int32_t[size];
    std::int32_t* copy2 = new std::int32_t[size];
    std::int32_t* copy3 = new std::int32_t[size];
    std::int32_t* copy4 = new std::int32_t[size];


    // Non-neon version
    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
    for (int i = 0; i < size; ++i)
    {
        copy[i] = orig[i];
    }
    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    std::cout << "Baseline time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;

    // NEON version
    begin = std::chrono::steady_clock::now();
    int32x4_t tmp;
    for (int i = 0; i < size; i += 4)
    {
        tmp = vld1q_s32(orig + i); // load 4 elements to tmp SIMD register
        vst1q_s32(&copy2[i], tmp); // copy 4 elements from tmp SIMD register
    }
    end = std::chrono::steady_clock::now();
    std::cout << "NEON time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;


    // Memcpy example
    begin = std::chrono::steady_clock::now();
    for (int i = 0; i < size; i+=4)
    {
        memcpy(orig+i, copy3+i, 4);
    }
    end = std::chrono::steady_clock::now();
    std::cout << "memcpy time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;


    // Memcpy example
    begin = std::chrono::steady_clock::now();
    memcpy(orig, copy4, size);
    end = std::chrono::steady_clock::now();
    std::cout << "memcpy time = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;

    return 0;
}