【问题标题】:Why the performance of combine7 is better than combine5? [closed]为什么combine7的性能优于combine5? [关闭]
【发布时间】:2013-04-22 15:20:00
【问题描述】:

我测试了以下7个函数,我不明白为什么combine7比combine5好。因为它们仅在“()”的位置上有所不同。

谁能给我解释一下?

这是我的代码:

#include "Common.h"

#define PLUS
#ifdef PLUS
#define INDENT 0
#define OP +
#else
#define INDENT 1
#define OP *
#endif
typedef int data_t;
typedef struct
{
    long int len;
    data_t *data;
}vec_rec, *vec_ptr;
vec_ptr new_vec(long int len)
{
    vec_ptr result = (vec_ptr)malloc(sizeof(vec_rec));  //Allocate header structure
    if(!result) return NULL;
    result->len = len;
    if(len > 0)     //Allocate array
    {
        data_t* data = (data_t*)calloc(len, sizeof(data_t));
        if(!data)
        {
            free((void*)result);
            return NULL;
        }
        result->data = data;
    }
    else result->data = NULL;
    return result;
}
int get_vec_element(vec_ptr v, long int index, data_t * dest)
{
    if(index < 0 || index >= v->len) return 0;
    *dest = v->data[index];
    return 1;
}
long int vec_length(vec_ptr v)
{
    return v->len;
}
data_t* get_vec_start(vec_ptr v)
{
    return v->data;
}

void combine5(vec_ptr v, data_t* dest)  
{
    long int i;
    long int length = vec_length(v);
    long int limit = length - 1;
    data_t* data = get_vec_start(v);
    data_t acc = INDENT;

    for(i = 0; i < limit; i += 2)
    {
        acc = (acc OP data[i]) OP data[i + 1];  
    }
    for(; i < length; i++)
        acc = acc OP data[i];
    *dest = acc;
}

void combine7(vec_ptr v, data_t* dest)
{
    long int i;
    long int length = vec_length(v);
    long int limit = length - 1;
    data_t* data = get_vec_start(v);
    data_t acc = INDENT;

    for(i = 0; i < limit; i += 2)
    {
        acc = acc OP (data[i] OP data[i + 1]);
    }
    for(; i < length; i++)
        acc = acc OP data[i];
    *dest = acc;
}

std::mt19937 gen;
int roll_die() {

    std::uniform_int_distribution<> dist(1, 6);

    return dist(gen);

}

int main()
{
    const size_t len = 10000000;
    auto vec_pointer = new_vec(len);

    std::generate(vec_pointer->data, vec_pointer->data + vec_pointer->len, roll_die);
    std::cout << "Initialized datas..." << std::endl;
    /*std::copy(vec_pointer->data, vec_pointer->data + vec_pointer->len, 
        std::ostream_iterator<int>(std::cout, "\t"));*/

    data_t dest = 0;

    utility::CStopwatch stopwatch5;
    combine5(vec_pointer, &dest);
    std::cout << "combine5 elapsed time(microseconds): " << stopwatch5.NowInMicro() << std::endl;

    utility::CStopwatch stopwatch7;
    combine7(vec_pointer, &dest);
    std::cout << "combine7 elapsed time(microseconds): " << stopwatch7.NowInMicro() << std::endl;
}

这是我的结果:

Initialized datas...
combine5 elapsed time(microseconds): 16934
combine7 elapsed time(microseconds): 14858

【问题讨论】:

  • 请提出具体问题。有 (7 2) 个答案。
  • 猜6有错字?不是acc0 = acc0 OP data[i]; acc1 = acc1 OP data[i+1];吗?
  • 您使用的是什么级别的优化?我认为在某些情况下,通过适当的优化(例如循环展开),时间差异会更小。但是,也许我期望很多形式的编译器?

标签: c++ visual-studio-2010 algorithm visual-c++ stl


【解决方案1】:

acc = (acc OP data[i]) OP data[i + 1];自然比
acc = acc OP (data[i] OP data[i + 1]);

因为在第一种情况下,您尝试在不同的操作中访问数据元素 data[i] 和 data[i+1],这会导致显着的开销,而在第二种情况下,您尝试同时访问它们(data[i] OP data[i + 1]) 的操作,因为它们是相邻的内存位置,并且相互迭代比在单独的时刻访问它们相对更快。

【讨论】:

    【解决方案2】:

    如果您真的有兴趣找出导致不同函数执行不同的原因,那么分析编译器生成的汇编代码可能是一个想法。这些函数非常简单,可以在汇编中阅读,即使是通常不习惯的人也是如此。

    在函数 3 中,您在每次迭代中取消引用:

    for(i = 0; i < length; i ++)
    {
        *dest = *dest OP data[i];   
    }
    

    在函数 4 中,您只在最后取消引用:

    for(i = 0; i < length; i ++)
    {
        acc = acc OP data[i];
    }
    *dest = acc;
    

    函数 5 更快,因为它只迭代一半的迭代次数。见:Loop unwinding

    【讨论】:

    • 我仍然想知道为什么取消引用会有所不同,我认为优化编译器只会在循环内使用寄存器进行加法,然后在循环/函数结束时将结果写回?
    • @ted 优化器可以做很多事情,但是由于我们没有被告知任何关于优化级别的信息,我认为取消引用似乎是一个合乎逻辑的罪魁祸首。我的 MSVC2010 示例程序集输出表明,如果不启用优化,将取消引用移到循环之外确实会有所不同。但是,是的,它应该是一个微小的变化,并且只有在没有优化的情况下才能看到。 :)
    【解决方案3】:

    不清楚为什么这些应该有很大不同(当然,fatih_k 的解释不能说服我)。由于您的运算符是可交换的,因此编译器可能想要更改顺序(取决于编译器标志)。您是否尝试过不同的编译器标志(特别是优化标志)和不同的编译器(clang、gcc、icpc)?

    另外,下面的循环体形式如何?

     {
        acc *= data[i];
        acc *= data[i+1];
     }
    

    附注:避免使用那些糟糕的宏。改为编写模板代码。

    【讨论】:

      猜你喜欢
      • 2013-01-14
      • 2014-06-08
      • 1970-01-01
      • 2014-05-14
      • 1970-01-01
      • 2019-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多