为什么这个C ++函数需要4倍于c函数

时间:2013-06-22 21:47:41

标签: c++ c

我正在考虑将c ++用于性能关键应用程序。我认为C和C ++都有相似的运行时间。但是我发现c ++函数需要运行4次>才能运行类似的C片段。

当我进行反汇编时,我看到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;
}

3 个答案:

答案 0 :(得分:7)

值得注意的是,您使用的数据结构并不完全等效。您的C列表实现为直接元素列表。您的C ++列表实现为指向实际元素的指针列表。为什么要将C ++列表列为指针

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

我猜你有时间调试你的代码版本,甚至可能用该库的调试版编译。

答案 1 :(得分: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));

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

答案 2 :(得分:4)

我复制了你的代码并运行10,000个元素列表的失败(因此完成)搜索时间:

没有优化:

  • TAILQ_FOREACH 0.717s
  • std::list<pfx_t *> 2.397s
  • std::list<pfx_t> 1.98s

(请注意,我为next pfx_t添加TAILQ,并使用与std::list相同的冗余结构

您可以看到指针列表比对象列表更糟糕。现在进行优化:

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

正如大家所指出的,优化是使用集合类型的紧密内循环中的主导术语。即使是最慢的变化也比最快的未优化版本快。也许更令人惊讶的是胜利者的变化 - 这可能是由于编译器更好地识别std代码中的优化机会而不是OS提供的宏。