Оптимизация шаблонов C++
В какой момент / может ли часть о методе шаблона быть оптимизирована компилятором? Будет ли это удалить недоступный код, развернуть ненужные циклы? (Биты используют неподписанные int- блоки, Integer использует неподписанные длинные)
Плюс, есть ли тип данных C++, означающий "я целое число размера ваших реестров процессоров"?
template<size_t bits> class IntegerFactoryImpl : public IntegerFactory<Integer<bits>>{
private:
template<int sizeOfLong, int sizeOfInt> Integer<bits> getOne(const Bits& b) const{
Integer<bits> integer = this->getOne();
size_t roof = (b.blocks() > integer.size()*(sizeOfLong/sizeOfInt))? integer.size()*(sizeOfLong/sizeOfInt) : b.blocks();
for(size_t i = 0; i < roof; ++i){
integer.at(i/(sizeOfLong/sizeOfInt)) = 0;
for(size_t j = 0; j < (sizeOfLong/sizeOfInt); ++j){
if(i % (sizeOfLong/sizeOfInt) == j){
integer.at(i/(sizeOfLong/sizeOfInt)) |= ((unsigned long)b.block(b.blocks()-i-1)) << (sizeOfInt*j);
break;
}
}
}
for(size_t i = roof; i < integer.size()*(sizeOfLong/sizeOfInt); ++i){
if(i % (sizeOfLong/sizeOfInt) == 0){
integer.at(i/(sizeOfLong/sizeOfInt)) = 0;
}
}
return integer;
}
public:
virtual ~IntegerFactoryImpl() throw(){}
virtual Integer<bits> getOne() const{
return Integer<bits>();
}
virtual Integer<bits> getOne(const Bits& b) const{
return this->getOne<sizeof(unsigned long)*8, sizeof(unsigned int)*8>(b);
}
};
Будет ли разница с этим кодом (без использования шаблона):
template<size_t bits> class IntegerFactoryImpl : public IntegerFactory<Integer<bits>>{
public:
virtual ~IntegerFactoryImpl() throw(){}
virtual Integer<bits> getOne() const{
return Integer<bits>();
}
virtual Integer<bits> getOne(const Bits& b) const{
Integer<bits> integer = this->getOne();
size_t roof = (b.blocks() > integer.size()*((sizeof(unsigned long)/sizeof(unsigned int)))? integer.size()*((sizeof(unsigned long)/sizeof(unsigned int)) : b.blocks();
for(size_t i = 0; i < roof; ++i){
integer.at(i/((sizeof(unsigned long)/sizeof(unsigned int))) = 0;
for(size_t j = 0; j < ((sizeof(unsigned long)/sizeof(unsigned int)); ++j){
if(i % ((sizeof(unsigned long)/sizeof(unsigned int)) == j){
integer.at(i/((sizeof(unsigned long)/sizeof(unsigned int))) |= ((unsigned long)b.block(b.blocks()-i-1)) << ((sizeof(unsigned int)*8)*j);
break;
}
}
}
for(size_t i = roof; i < integer.size()*((sizeof(unsigned long)/sizeof(unsigned int)); ++i){
if(i % ((sizeof(unsigned long)/sizeof(unsigned int)) == 0){
integer.at(i/((sizeof(unsigned long)/sizeof(unsigned int))) = 0;
}
}
return integer;
}
};
(редактировать: я только что обнаружил, что код не работает должным образом (я исправил это), но оригинальный вопрос все еще применяется..)
1 ответ
Правильно, компилятор оптимизирует все, что он может вычислить во время компиляции, и если у вас есть цикл, который повторяется только один раз (например, for(i = 0; i < 1; i++)
, это удалит петлю полностью.
Что касается целых размеров, это действительно зависит от того, что вы пытаетесь достичь, если лучше использовать long
или же int
, Например, в x86-64 64-битная операция займет дополнительный байт, чтобы указать, что следующая инструкция является 64-битной, а не 32-битной. Если компилятор сделал int
Длиной 64 бита код становился (немного) больше и, следовательно, не очень хорошо подходил бы для кешей и т. Д. И т. Д. Нет преимущества в скорости между 16-, 32- или 64-битными операциями [для 99% операции, умножение и деление являются некоторыми из очевидных исключений - чем больше число, тем больше времени требуется, чтобы разделить или умножить его ((На самом деле, количество битов SET в числе влияет на время умножения, и я считаю, что деление тоже)) ] в x86-64. Конечно, если вы, например, используете значения для выполнения операций битовой маски и тому подобное, используйте long
предоставит вам 64-битные операции, для выполнения которых требуется вдвое меньше операций. Это явно преимущество. Так что это "правильно" использовать long
в этом случае, даже если он добавляет дополнительный байт на инструкцию.
Также имейте в виду, что очень часто, int
используется для "меньших чисел", поэтому для многих вещей дополнительный размер int
будет просто потрачен впустую, и займет дополнительное место в кэше данных и т. д., и т. д. int
остается 32-разрядным также для сохранения размера больших целочисленных массивов и тому подобного на разумном размере.