Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.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 _mm_sad_epu8比_mm_sad_pu8快_C_Sse_Intrinsics - Fatal编程技术网

C _mm_sad_epu8比_mm_sad_pu8快

C _mm_sad_epu8比_mm_sad_pu8快,c,sse,intrinsics,C,Sse,Intrinsics,在基准测试中,128位固有函数的执行速度比64位固有函数快 _mm_sad_epu8(__m128i, __m128i) //Clocks: 0.0300 _mm_sad_pu8(__m64, __m64) //Clocks: 0.0491 据我所知,《英特尔参考手册》指出,mmx寄存器上的(PSADBW)延迟为5,吞吐量为1,但未说明mm寄存器的性能 它们不是同样快吗?这对于采用128位参数的内在函数来说是通用的吗?我的测量程序(见下文)显示,在Core-i5 3450(常春藤桥)

在基准测试中,128位固有函数的执行速度比64位固有函数快

_mm_sad_epu8(__m128i, __m128i) //Clocks: 0.0300
_mm_sad_pu8(__m64, __m64)      //Clocks: 0.0491
据我所知,《英特尔参考手册》指出,mmx寄存器上的
(PSADBW)
延迟为5,吞吐量为1,但未说明mm寄存器的性能

它们不是同样快吗?这对于采用128位参数的内在函数来说是通用的吗?

我的测量程序(见下文)显示,在Core-i5 3450(常春藤桥)上,
\u mm\u sad\u epu8
的性能与
\u mm\u sad\u pu8
相当。后者甚至稍微快一点

我的程序的输出是:

warmup:              1.918 sec total
measure_mm_sad_epu8: 1.904 sec total, 0.372 nsec per operation
measure_mm_sad_pu8:  1.872 sec total, 0.366 nsec per operation
我的处理器的turbo时钟频率为3.5 GHz(单线程),
\u mm\u sad\u epu8
的吞吐量应为1个时钟周期。因此,每个操作应至少花费0.286纳秒。所以我的测量程序达到了最高性能的77%

我使用Visual Studio C++ 2010 Express,并创建了一个新的Win32控制台应用程序。该程序已使用标准的“发布”设置编译。这是cpp文件的代码:

#include "stdafx.h"
#include <cassert>
#include <ctime>
#include <iostream>
#include <iomanip>

extern "C" {
  #include <emmintrin.h>
}

float measure_mm_sad_epu8(int n, int repeat) {
    assert(n % 16 == 0);
    // Didn't get an aligned "new" to work :-(
    __m128i *input  = (__m128i *) _aligned_malloc(n * sizeof *input,  16);
    __m128i *output = (__m128i *) _aligned_malloc(n * sizeof *output, 16);
    if(!input || !output) exit(1);
    __m128i zero = _mm_setzero_si128();

    for(int i=0; i < n; i++) {
        input[i].m128i_i64[0] = 0x0123456789abcdef;
        input[i].m128i_i64[1] = 0xfedcba9876543210;
    }

    clock_t startTime = clock();
    for(int r = 0; r < repeat; r++) {
        for(int i = 0; i < n; i+=16) { // loop unrolled
            output[i  ] = _mm_sad_epu8(input[i  ], zero);
            output[i+1] = _mm_sad_epu8(input[i+1], zero);
            output[i+2] = _mm_sad_epu8(input[i+2], zero);
            output[i+3] = _mm_sad_epu8(input[i+3], zero);
            output[i+4] = _mm_sad_epu8(input[i+4], zero);
            output[i+5] = _mm_sad_epu8(input[i+5], zero);
            output[i+6] = _mm_sad_epu8(input[i+6], zero);
            output[i+7] = _mm_sad_epu8(input[i+7], zero);
            output[i+8] = _mm_sad_epu8(input[i+8], zero);
            output[i+9] = _mm_sad_epu8(input[i+9], zero);
            output[i+10] = _mm_sad_epu8(input[i+10], zero);
            output[i+11] = _mm_sad_epu8(input[i+11], zero);
            output[i+12] = _mm_sad_epu8(input[i+12], zero);
            output[i+13] = _mm_sad_epu8(input[i+13], zero);
            output[i+14] = _mm_sad_epu8(input[i+14], zero);
            output[i+15] = _mm_sad_epu8(input[i+15], zero);
        }
    }
    _mm_empty();
    clock_t endTime = clock();

    _aligned_free(input);
    _aligned_free(output);
    return (endTime-startTime)/(float)CLOCKS_PER_SEC;
}

float measure_mm_sad_pu8(int n, int repeat) {
    assert(n % 16 == 0);
    // Didn't get an aligned "new" to work :-(
    __m64 *input  = (__m64 *) _aligned_malloc(n * sizeof *input,  16);
    __m64 *output = (__m64 *) _aligned_malloc(n * sizeof *output, 16);
    if(!input || !output) exit(1);
    __m64 zero = _mm_setzero_si64();

    for(int i=0; i < n; i+=2) {
        input[i  ].m64_i64 = 0x0123456789abcdef;
        input[i+1].m64_i64 = 0xfedcba9876543210;
    }

    clock_t startTime = clock();
    for(int r = 0; r < repeat; r++) {
        for(int i = 0; i < n; i+=16) { // loop unrolled
            output[i  ] = _mm_sad_pu8(input[i  ], zero);
            output[i+1] = _mm_sad_pu8(input[i+1], zero);
            output[i+2] = _mm_sad_pu8(input[i+2], zero);
            output[i+3] = _mm_sad_pu8(input[i+3], zero);
            output[i+4] = _mm_sad_pu8(input[i+4], zero);
            output[i+5] = _mm_sad_pu8(input[i+5], zero);
            output[i+6] = _mm_sad_pu8(input[i+6], zero);
            output[i+6] = _mm_sad_pu8(input[i+7], zero);
            output[i+7] = _mm_sad_pu8(input[i+8], zero);
            output[i+8] = _mm_sad_pu8(input[i+9], zero);
            output[i+10] = _mm_sad_pu8(input[i+10], zero);
            output[i+11] = _mm_sad_pu8(input[i+11], zero);
            output[i+12] = _mm_sad_pu8(input[i+12], zero);
            output[i+13] = _mm_sad_pu8(input[i+13], zero);
            output[i+14] = _mm_sad_pu8(input[i+14], zero);
            output[i+15] = _mm_sad_pu8(input[i+15], zero);
        }
    }
    _mm_empty();
    clock_t endTime = clock();

    _aligned_free(input);
    _aligned_free(output);
    return (endTime-startTime)/(float)CLOCKS_PER_SEC;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int n = 256, repeat = 20000000;
    float time;

    std::cout << std::setprecision(3) << std::fixed;

    time = measure_mm_sad_epu8(n,repeat);
    std::cout << "warmup:              " << time << " sec total" << std::endl;
    time = measure_mm_sad_epu8(n,repeat);
    std::cout << "measure_mm_sad_epu8: " << time << " sec total, " << time/n/repeat*1e9 << " nsec per operation" << std::endl;

    n*=2;      // same memory footprint
    repeat/=2; // but with same amount of calculations
    time = measure_mm_sad_pu8(n,repeat);
    std::cout << "measure_mm_sad_pu8:  " << time << " sec total, " << time/n/repeat*1e9 << " nsec per operation" << std::endl;
    return 0;
}
IvyBridge有足够的(管道)端口来“同时”执行此操作。 生成的代码仅使用
xmm1
xmm0
寄存器,因此它依赖于处理器的寄存器重命名EDIT2:由于地址算法的变化,代码长度从13字节到20字节不等。因此,代码可能会遇到解码器瓶颈,因为常春藤网桥每个时钟周期只能解码16字节(最多4条指令)。另一方面,它有一个循环缓存来处理这个问题

MMX版本生成的代码几乎相同:

013412D4  movq        mm1,mmword ptr [ecx-18h]  
013412D8  psadbw      mm1,mm0  
013412DB  movq        mmword ptr [eax-8],mm1  

013412DF  ...

这两个版本的内存占用量都是2*4kib,因为我将MMX版本中的元素数量增加了一倍(参见main)。

您是如何测量执行时间的?clock()/CLOCKS\u PER\u SECOk,那么:基准循环是否运行了几秒钟?处理器是否达到相同的涡轮频率?它实际上是哪个处理器?我已经尝试了几秒钟的不同循环迭代。我还尝试对语句进行重新排序,以便消除潜在的turbo CPU功能。请链接提到的英特尔参考手册。我查过这个,也查过这个。它们都没有定义使用MM寄存器时的计时。感谢您的大量工作。这段代码或多或少等于我创建的基准程序。然而,我是用Xcode编译的,它可能来自不同的编译设置。这并不完全适合寄存器,所以每个
psadbw
都有一个加载和存储。将其写入
\u mm\u sad\u epu8(零,输入[i+1])
(args以另一个顺序)确实像clang一样令人信服(当以AVX为目标时,3个操作数的非破坏性操作允许该操作在不影响调零向量reg的情况下工作。)无论如何,这可能仍然会使IvB上的端口0与psadbw饱和。也许MMX版本较小的缓存占用空间会造成微小的差异:如果被逐出,需要重新填充的缓存线会更少?@PeterCordes我在回答中添加了一些关于生成代码的更多细节。这两个版本的缓存占用空间是相同的,因为我将MMX版本中的元素数量增加了一倍(请参阅main)。我的编译器不支持折叠。您使用了64位编译器。我现在只有一个32位的编译器-(哦,我明白了,你实际上是在一个更大的缓冲区上循环。我原以为代码只是在一个16个元素的数组上循环,假设你希望它发出一个只包含
psadbw
指令的循环,没有任何可能成为瓶颈的加载/存储。所以我没有仔细阅读,看不出你是这样的干什么!
013410D0  movdqa      xmm1,xmmword ptr [ecx-30h]  
013410D5  psadbw      xmm1,xmm0  
013410D9  movdqa      xmmword ptr [eax-10h],xmm1  

013410DE  ...
013412D4  movq        mm1,mmword ptr [ecx-18h]  
013412D8  psadbw      mm1,mm0  
013412DB  movq        mmword ptr [eax-8],mm1  

013412DF  ...