У меня есть одна строка кода, которая потребляет 25% - 30% времени выполнения моего приложения. Это меньше, чем компаратор для std:: set (набор реализован с помощью Red-Black-Tree). Он называется около 180 миллионов раз в течение 28 секунд.
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;
}
}
};
Записи должны сортироваться по стоимости, и если они совпадают по их идентификатору. У меня есть много вставок для каждого извлечения минимума. Я думал об использовании 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 = С++ 0x) на многоядерной машине intel. Доступны только доступные библиотеки: openmp и tbb. 30-секундный тест был выполнен на моем 4-летнем ноутбуке (ядро 2-го дуэта).
Я действительно застрял на этом, это кажется таким простым, но занимает много времени. Я хрустел головой, так как дни думали, как я могу улучшить эту линию...
Можете ли вы дать мне предложение, как улучшить эту часть, или это уже в лучшем случае?
EDIT 1: После использования предложения Jerrys я достиг скорости ~ 4,5 секунды. РЕДАКТИРОВАТЬ 2: после попытки увеличить количество бит Фибоначчи, сравнение перешло к 174 Mio-вызовам к функции меньше.