C++ C++;优化if/else条件
我只有一行代码,它占用了应用程序运行时的25%-30%。它是std::set的小于比较器(set是用红黑树实现的)。 它在28秒内被调用了大约1.8亿次C++ C++;优化if/else条件,c++,performance,assembly,C++,Performance,Assembly,我只有一行代码,它占用了应用程序运行时的25%-30%。它是std::set的小于比较器(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> st
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;
}
}
};
struct条目{
施工浮动成本;
const long_id;
//其他一些Var
分录(浮动成本,浮动id):\u成本(成本),\u id(id){
}
};
模板
结构lt_项:公共二进制函数
{
布尔运算符()(常数T&l,常数T&r)常数
{
//最易读的形状
如果(l._成本!=r._成本){
返回剩余成本<剩余成本;
}否则{
返回l.\u id
条目应按成本排序,如果成本相同,则按其id排序。
对于最小值的每次提取,我有许多插入。我曾考虑过使用斐波那契堆,但有人告诉我,它们理论上很好,但存在高常数问题,而且实现起来相当复杂。由于insert是在O(log(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);
返回l.|成本l.|成本| l.| 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;
typedef联合{
浮动(f),;
int_i;
}燧石;
//...
燧石差;
差异f=(l._成本-r._成本);
返回(diff._i和&diff._i>>31)| l._id
但是编译器似乎已经足够聪明了,因为我还没有能够改进运行时
我也考虑过苏格兰和南方能源公司,但这个问题实际上并不适用于苏格兰和南方能源公司
部件看起来有点像这样:
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
movss(%rbx),%xmm1
mov$0x1,%r8d
MOVS 0x20(%rdx),%xmm0
UCOMIST%xmm1,%xmm0
ja 0x410600
UCOMIST%xmm0,%xmm1
jp 0x4105fd
jne 0x4105fd
mov 0x28(%rdx),%rax
cmp%rax,0x8(%rbx)
jb 0x410600
异或%r8d,%r8d
我对汇编语言有一点点的经验,但不是很多
我认为挤出一些表现是最好的(唯一的?),但这真的值得努力吗?你能看到任何可以节省一些周期的快捷方式吗
代码将在多核intel机器上运行的平台是带有GCC4.6(-stl=c++0x)的Ubuntu12。只有boost、openmp和tbb库可用。30秒的基准测试是在我4岁的笔记本电脑(Core2Duo)上进行的
我真的被困在这个问题上了,它看起来很简单,但却花了那么多时间。几天以来,我一直在绞尽脑汁思考如何改进这条线路
你能给我一个如何改进这部分的建议吗,或者说它已经处于最佳状态了
编辑1:使用Jerry的建议后,我的速度提高了约4.5秒。
编辑2:在尝试boost Fibonacci堆之后,对比了1.74亿次对小于函数的调用。我本身没有答案-只有几个想法:
我很难相信: a) 比较功能在30秒内运行1.8亿次 及 b) 比较函数使用25%的cpu时间 都是真的。即使是Core 2 Duo也应该能够在不到一秒钟的时间内轻松运行1.8亿次比较(毕竟,有人声称它可以实现12000次MIPS,如果这真的意味着什么的话)。因此,我倾向于相信,在分析软件的比较中,还有其他一些东西。(例如,为新元素分配内存。)
但是,您至少应该考虑一下STD::SET不是您正在寻找的数据结构的可能性。如果在实际需要排序值(甚至是最大值)之前进行了数百万次插入,那么最好将这些值放入向量中,这是一种在时间和空间上都非常便宜的数据结构,并根据需要对其进行排序
如果你真的需要这个集合,因为你担心冲突,那么你可能会考虑一个无序的集合,它稍微便宜,但不像向量那么便宜。(正是因为向量不能保证你的唯一性。)但老实说,看看这个结构定义,我很难相信唯一性对你很重要
“基准” 在我的小型Core i5笔记本电脑上(我想它与OP的机器不在同一个级别),我运行了一些测试,将1000万个随机唯一条目(仅包含两个比较字段)插入到std::set和std::vector中。最后,我对向量进行排序 我做了两次;一次使用随机生成器产生可能唯一的成本,一次使用生成器产生正好两种不同的成本(这会使比较变慢)。1000万次插入的结果比OP报告的结果略多 unique cost discrete cost
compares time compares time
set 243002508 14.7s 241042920 15.6s
vector 301036818 2.0s 302225452 2.3s
为了进一步隔离比较时间,我使用std::sort和std::partial_sort重新定义了向量基准,使用了10个元素(基本上是前10个元素的选择)和10%的元素(即一百万)。更大的部分_排序的结果让我惊讶——谁会想到对一个向量进行10%的排序会比对所有向量进行排序慢——但它们表明,算法成本比比较成本要重要得多:
unique cost discrete cost
compares time compares time
partial sort 10 10000598 0.6s 10000619 1.1s
partial sort 1M 77517081 2.3s 77567396 2.7s
full sort 301036818 2.0s 302225452 2.3s
结论:比较时间越长,但以容器操作为主。在总共52秒的计算时间内,1000万次集合插入的总成本是显而易见的。1000万个向量插入的总成本就不那么引人注目了
小纸条,不管它值多少钱:
我从汇编代码中得到的一点是,你不是savin
struct Entry
{
double cost_;
long id_;
long long sortingId_;
// some other vars
Entry( double cost, float id )
: cost_( cost ), id_( id ), sortingId_( 1e9*100*cost + id )
{}
};
struct __attribute__ ((__packed__)) __attribute__((aligned(8)) Entry {
// Do *not* reorder the following two fields or comparison will break.
const int32_t _id;
const float _cost;
// some other vars
Entry(long id, float cost) : _cost(cost), _id(id) {}
};
bool operator<(Entry const &a, Entry const &b) {
return *(int64_t const *)&a < *(int64_t const *)&b;
}
int main() {
Entry a(1236, 1.234f), b(1234, 1.235f), c(1235, 1.235f);
std::cout << std::boolalpha;
std::cout << (b<a) << "\n";
std::cout << (a<b) << "\n";
std::cout << (b<c) << "\n";
std::cout << (c<b) << "\n";
return 0;
}
false
true
true
false