【发布时间】:2012-12-12 03:44:57
【问题描述】:
我有一行代码,占用了我的应用程序运行时间的 25% - 30%。它是 std::set 的小于比较器(该集合是用红黑树实现的)。 它在 28 秒内被调用了大约 1.8 亿次。
struct Entry {
const float _cost;
const long _id;
// some other vars
Entry(float cost, float id) : _cost(cost), _id(id) {
}
};
template<class T>
struct lt_entry: public binary_function <T, T, bool>
{
bool operator()(const T &l, const T &r) const
{
// Most readable shape
if(l._cost != r._cost) {
return r._cost < l._cost;
} else {
return l._id < r._id;
}
}
};
条目应按成本排序,如果成本相同,则按其 id 排序。 对于最小值的每次提取,我都有很多插入。我考虑过使用 Fibonacci-Heaps,但有人告诉我,它们在理论上很好,但常数很高,实现起来相当复杂。并且由于插入在 O(log(n)) 中,因此运行时间增加在 n 较大时几乎是恒定的。所以我认为坚持这个系列是可以的。
为了提高性能,我尝试用不同的形状来表达它:
return l._cost < r._cost || r._cost > l._cost || l._id < r._id;
return l._cost < r._cost || (l._cost == r._cost && l._id < r._id);
即使这样:
typedef union {
float _f;
int _i;
} flint;
//...
flint diff;
diff._f = (l._cost - r._cost);
return (diff._i && diff._i >> 31) || l._id < r._id;
但编译器似乎已经足够聪明了,因为我无法改进运行时。
我也考虑过SSE,但是这个问题确实不太适用于SSE...
程序集看起来有点像这样:
movss (%rbx),%xmm1
mov $0x1,%r8d
movss 0x20(%rdx),%xmm0
ucomiss %xmm1,%xmm0
ja 0x410600 <_ZNSt8_Rb_tree[..]+96>
ucomiss %xmm0,%xmm1
jp 0x4105fd <_ZNSt8_Rb_[..]_+93>
jne 0x4105fd <_ZNSt8_Rb_[..]_+93>
mov 0x28(%rdx),%rax
cmp %rax,0x8(%rbx)
jb 0x410600 <_ZNSt8_Rb_[..]_+96>
xor %r8d,%r8d
我对汇编语言有一点经验,但不是很多。
我认为挤出一些性能是最好的(唯一?)点,但真的值得付出努力吗?你能看到任何可以节省一些周期的捷径吗?
代码将运行的平台是 ubuntu 12 和 gcc 4.6 (-stl=c++0x),在多核英特尔机器上。唯一可用的库是 boost、openmp 和 tbb。 30 秒基准测试是在我用了 4 年的旧笔记本电脑(core 2 duo)上执行的。
我真的坚持这个,它看起来很简单,但需要很多时间。几天来我一直在思考如何改进这条线......
您能否给我一个建议如何改进这部分,或者它已经处于最佳状态?
编辑 1:使用 Jerrys 的建议后,我实现了约 4.5 秒的加速。 编辑 2:在尝试提升 Fibonacci 堆后,比较到 174 次 Mio 调用小于函数。
【问题讨论】:
-
比较两个数字是您应用程序的瓶颈吗?哇...您在程序的其他部分几乎什么都没做。
-
您应该考虑您的分析软件在撒谎的可能性,并且瓶颈在此操作的上下几行。
-
顺便说一句,我认为 1.8*10^8 比较的 52s 是公平的数字。 RB-Tree 具有很大的隐藏常数,具有 P4 处理器的计算机每秒可以运行大约 10^7 次基本操作。
-
你到底在说什么? IE。你是如何分析它的?是分支未命中预测还是缓存未命中?非对齐访问?我建议您首先使用 vtune 或 perf 工具来解决这个问题。然后想想怎么解决。例如,可能值得重组以使其缓存友好,甚至使其适合 L1(或 L2)。到目前为止,您肯定会在填充上浪费内存。请记住,由于其他代码,您也可能会看到副作用,即使 CPU 在这里很糟糕,也不一定意味着该代码有问题。
-
我不知道速度,但是为了可读性,我喜欢std::tie:
return std::tie(l._cost, l.id) < std::tie(r._cost, r._id);
标签: c++ performance assembly