Вот некоторый код (полная программа следует далее в вопросе):
template <typename T>
T fizzbuzz(T n) {
T count(0);
#if CONST
const T div(3);
#else
T div(3);
#endif
for (T i(0); i <= n; ++i) {
if (i % div == T(0)) count += i;
}
return count;
}
Теперь, если я вызову эту шаблонную функцию с int
, тогда я получу разницу в 6 раз в зависимости от того, определю я CONST или нет:
$ gcc --version
gcc (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
$ make -B wrappedint CPPFLAGS="-O3 -Wall -Werror -DWRAP=0 -DCONST=0" &&
time ./wrappedint
g++ -O3 -Wall -Werror -DWRAP=0 -DCONST=0 wrappedint.cpp -o wrappedi
nt
484573652
real 0m2.543s
user 0m2.059s
sys 0m0.046s
$ make -B wrappedint CPPFLAGS="-O3 -Wall -Werror -DWRAP=0 -DCONST=1" &&
time ./wrappedint
g++ -O3 -Wall -Werror -DWRAP=0 -DCONST=1 wrappedint.cpp -o wrappedi
nt
484573652
real 0m0.655s
user 0m0.327s
sys 0m0.046s
Изучение дизассемблирования показывает, что в быстром (const) случае модуль превратился в вещь типа умножения и сдвига, тогда как в медленном (неконстантном) случае используется idivl
.
Еще хуже, если я попытаюсь обернуть мое целое число в классе, тогда оптимизация не произойдет, использую ли я const или нет. Код всегда использует idivl
и работает медленно:
#include <iostream>
struct WrappedInt {
int v;
explicit WrappedInt(const int &val) : v(val) {}
bool operator<=(const WrappedInt &rhs) const { return v <= rhs.v; }
bool operator==(const WrappedInt &rhs) const { return v == rhs.v; }
WrappedInt &operator++() { ++v; return *this; }
WrappedInt &operator+=(const WrappedInt &rhs) { v += rhs.v; return *this; }
WrappedInt operator%(const WrappedInt &rhs) const
{ return WrappedInt(v%rhs.v); }
};
std::ostream &operator<<(std::ostream &s, WrappedInt w) {
return s << w.v;
}
template <typename T>
T fizzbuzz(T n) {
T count(0);
#if CONST
const T div(3);
#else
T div(3);
#endif
for (T i(0); i <= n; ++i) {
if (i % div == T(0)) count += i;
}
return count;
}
int main() {
#if WRAP
WrappedInt w(123456789);
std::cout << fizzbuzz(w) << "\n";
#else
std::cout << fizzbuzz<int>(123456789) << "\n";
#endif
}
Мои вопросы:
1) Существует ли простой принцип самого C ++ или оптимизации gcc, объясняющий, почему это происходит, или это всего лишь случай «запуска различных эвристик, это код, который вы получаете»?
2) Есть ли способ заставить компилятор понять, что мой локально объявленный и никогда не упоминаемый const WrappedInt может рассматриваться как значение const во время компиляции? Я хочу, чтобы эта вещь была прямой заменой int в шаблонах.
3) Известен ли способ обёртывания int таким образом, что компилятор может отбрасывать обёртку при оптимизации? Цель состоит в том, что WrappedInt будет основанным на политике шаблоном. Но если политика «ничего не делает» приводит к произвольным 6-кратным штрафам за превышение скорости по сравнению с int, я бы лучше использовал специальный случай и использовал int напрямую.