-1

我正在考虑将 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;
}
4

3 回答 3

7

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

当然,仅此一项不会导致性能出现四倍的差异。但是,它可能会影响代码的性能,因为它的内存位置更差。

我猜你对代码的调试版本进行了计时,甚至可能使用库的调试版本进行编译。

于 2013-06-22T21:55:46.283 回答
4

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

没有优化:

  • TAILQ_FOREACH0.717s
  • std::list<pfx_t *>2.397s
  • std::list<pfx_t>1.98s

(请注意,我将 anext放入pfx_tforTAILQ并使用与 相同的冗余结构std::list

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

  • TAILQ_FOREACH0.467s
  • std::list<pfx_t *>0.553s
  • std::list<pfx_t>0.345s

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

于 2013-06-22T22:23:34.820 回答
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));

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

于 2013-06-22T22:12:03.030 回答