2009-10-30 2 views
2

У меня есть контейнер, похожий на этот.Странная проблема с производительностью

template <typename Nat, typename Elt> 
class NatMap { 
public: 
    Elt& operator[] (Nat nat) { 
    return tab [nat.GetRaw()]; 
    } 
private: 
    Elt tab [Nat::kBound]; 
}; 

Я хотел отказаться от требования для Ег, чтобы иметь конструктор по умолчанию:

template <typename Nat, typename Elt> 
class NatMap { 
public: 
    Elt& operator[] (Nat nat) { 
    return ((Elt*)tab) [nat.GetRaw()]; 
    } 
private: 
    char tab [Nat::kBound * sizeof(Elt)]; 
}; 

Я использую г ++ - 4,3 и этот код работает 25% медленнее в моем приложении, чем предыдущий. К сожалению, замедление не проявляется в синтетическом бенчмарке. Я думаю, что это что-то о оптимизации компилятора, псевдониме, выравнивании или подобном материале.

Что мне делать, чтобы вернуть свое исполнение? (Пока не требуется конструктор по умолчанию)

Update:

Только теперь я попробовал новый г ++ - 4,4 и он дал мне следующее предупреждение для последнего кода:

dereferencing pointer '<anonymous>' does break strict-aliasing rules 
+0

Вы используете «размещение нового» или какой-нибудь другой уродливый взлом? Нежелание по умолчанию ctor можно обойти, определив хотя бы один ctor. – dirkgently

+0

Размещение нового не является уродливым взломом, но является важной особенностью языка. Обычно он красиво завернут в контейнер, как вектор, поэтому люди думают, что это неясно. Пожалуйста, не делайте этого, это не более чем способ вызова конструктора существующего объекта. –

+0

И чтобы ответить на ваш вопрос, я не использую новое место размещения, но я буду, поскольку мне нужно реализовать контейнер, который поддерживает объекты, которые имеют только имена конструкторов публично. –

ответ

0

небольшое предложение: вместо того, чтобы пытаться сделать обоснованные догадки, например, если оптимизация компилятора отличается, вы можете либо выполнить одноэтапное, либо узнать with this unorthodox method.

+0

Майк, я не ищу медленную часть алгоритма.Доступ к массиву очень важен, и я хочу знать, почему в этом случае я получаю на 25% медленнее двоичный файл. –

+0

В этом случае один шаг по каждой программе на уровне инструкций - это простой способ узнать. Они делают разные вещи. Выборка также расскажет вам. Возможно, я вас неправильно понял. Может быть, вы знаете, что они делают по-другому, и вы только хотите знать, почему. –

+0

... На самом деле, иногда я запускаю две программы бок о бок, в двух экземплярах IDE/отладчика, поэтому я вижу, как они отличаются. Это может быть немного медленнее, но, черт возьми, я тоже :-) –

1

Возможно, вы столкнулись с проблемами выравнивания. Если Elt имеет некоторый размер, отличный от собственного типа выравнивания, то выделение его посредством размещения в массив символов может включать в себя множество невыровненных чтений, которые вы не видите, когда компилятор выравнивает его для вас. Или вы можете столкнуться с проблемой, называемой load-hit-store, которую некоторые процессоры проявляют, когда записывают значение в память, а затем сразу же считывают ее; в этих процессорах он может быть стойлом до тех пор, пока конвейер.

Возможно, это может быть что-то еще, что-то вроде генерации патологического кода GCC.

К сожалению, трассировка стека не помогает отследить любую из этих проблем, поскольку они будут выглядеть как операция загрузки (lw, lb и т. Д.), Что заняло сорок циклов вместо одного. Стойка находится в микрокоде внутри CPU, а не в коде x86, который вы написали. Но просмотр сборки с помощью опции командной строки -S может помочь вам разобраться в том, что действительно испускает компилятор, и как он отличается от двух ваших реализаций. Может быть, в одной версии появляется какая-то плохая операция.

+0

Я буду смотреть на это, но Elt почти всегда имеет размер. __atributte__ ((aligin)) тоже не помогает. –

+0

атрибут (выравнивание) будет помечен новостями размещения в массив символов; в конце концов, вы могли бы сказать, что он помещается по любому произвольному адресу. – Crashworks