2013-06-22 37 views
-1

我正在考虑将C++用于性能关键应用程序。我认为C和C++都有可比的运行时间。但是我发现C++函数需要运行4次以上的可比的C代码片段。为什么此C++函数需要c函数4倍的时间

当我做了反汇编,我看到end(),++,!=都是作为函数调用实现的。是否有可能使他们(至少其中一些)内联?

这里是C++代码:

typedef struct pfx_s { 
    unsigned int start; 
    unsigned int end; 
    unsigned int count; 
} pfx_t; 

typedef std::list<pfx_t *> pfx_list_t; 

int 
eval_one_pkt (pfx_list_t *cfg, unsigned int ip_addr) 
{ 
    const_list_iter_t iter; 

    for (iter = cfg->begin(); iter != cfg->end(); iter++) { 
     if (((*iter)->start <= ip_addr) && 
      ((*iter)->end >= ip_addr)) { 
      (*iter)->count++; 
      return 1; 
     } 
    } 
    return 0; 
} 

而且这是等价的C代码:

int 
eval_one_pkt (cfg_t *cfg, unsigned int ip_addr) 
{ 
    pfx_t *pfx; 

    TAILQ_FOREACH (pfx, &cfg->pfx_head, next) { 
     if ((pfx->start <= ip_addr) && 
      (pfx->end >= ip_addr)) { 
      pfx->count++; 
      return 1; 
     } 
    } 
    return 0; 
} 
+8

您是否优化过?它应该内联他们,当你打开优化。 –

+0

C++会这样做,以允许运算符重载。一个优秀的优化者应该能够判断你是否实际上超载了他们并且不接电话。 –

+5

您报告的统计信息似乎意味着您调试了代码的调试版本,可能是调试版本的库。这种测量几乎没有意义。 – AnT

回答

7

这可能是值得指出的是,你使用的数据结构并不完全等同。您的C列表以直接元素列表的形式实现。您的C++列表实现为实际元素的指针列表。你为什么让你的C++列表列表指针

这当然不会导致性能的四倍差异。但是,它可能会影响代码的性能,导致其更糟糕的内存局部性。

我想你定时调试你的代码的版本,甚至可能编译调试版本的库。

+3

“当我做了反汇编时,我看到了end(),++,!=都是作为函数调用实现的。” - 我认为这更重要。 – 2013-06-22 22:01:45

+1

是的。与1)打开编译器优化和2)使C列表结构指针的性能非常接近C为34.5 vs C++为35.8 – helloworld

+0

其实,我不会惊讶地发现缓存不友好的列表指针引起四倍的性能差异。此外,由于pfx_s只有3个整数长,所以看到发生了什么(好或坏)和更大的结构也会很有趣。例如,与缓存行一样大,或更多。 – hmijail

4

你真的有有什么理由在这里使用一个列表?乍一看,它看起来像一个std::vector将是一个更好的选择。你可能也不想要一个指针的容器,只是一个对象的容器。

你也可以做的工作比较多一点整齐标准的算法:

typedef std::vector<pfx_t> pfx_list_t; 

int 
eval_one_pkt(pfx_list_t const &cfg, unsigned int ip_addr) { 
    auto pos = std::find_if(cfg.begin(), cfg.end(), 
     [ip_addr](pfx_t const &p) { 
      return ip_addr >= p.begin && ip_addr <= p.end; 
     }); 

    if (pos != cfg.end()) { 
     ++(pos->count); 
     return 1; 
    } 
    return 0; 
} 

如果我这样做,但是,我可能把它转换成通用的算法来代替:

template <class InIter> 
int 
eval_one_pkt(InIter b, InIter e, unsigned int ip_addr) { 
    auto pos = std::find_if(b, e, 
     [ip_addr](pfx_t const &p) { 
      return ip_addr >= p.begin && ip_addr <= p.end; 
     }); 

    if (pos != cfg.end()) { 
     ++(pos->count); 
     return 1; 
    } 
    return 0; 
} 

尽管与C与C++无关,但对于范围检查可能需要进一步优化,您可能需要尝试如下所示:

return ((unsigned)(ip_addr-p.begin) <= (p.end-p.begin)); 

在启用了优化的现代编译器的情况下,我期望模板在使用时完全内联扩展,因此可能根本不会涉及任何函数调用。

+0

感谢您的回答。列表是一个更好的选择,因为pfx_ts可以来去。我使用了对象指针,因为对象可以在多个列表中。 – helloworld

+1

@helloworld:[list仍然可能不是更好的选择](http://stackoverflow.com/a/9765090/179910)。对使用指针足够公平。 –

+0

感谢列表vs向量上的指针。我同意你的推理 – helloworld

4

我复制你的代码,跑10000时序失败(从而完成)1名万元列表的搜索:

没有优化:

  • TAILQ_FOREACH 0.717s
  • std::list<pfx_t *> 2.397s
  • std::list<pfx_t> 1。98S

(请注意,我把nextpfx_tTAILQstd::list使用相同的冗余结构)

你可以看到指针列表比对象的列表更糟。现在,随着优化:

  • TAILQ_FOREACH 0.467s
  • std::list<pfx_t *> 0.553s
  • std::list<pfx_t> 0.345s

所以大家都指出,优化利用集合类型在紧张的内环主项。即使最慢的变化速度也比最快的未优化版本更快。也许更让人惊讶的是获胜者的变化 - 这可能是由于编译器更好地识别std代码中的优化机会,而不是OS提供的宏。

+0

感谢您的回答。在对象需要位于多个列表中的情况下,对象列表不起作用。 – helloworld

相关问题