В функции PrintUint
он просто разворачивает цикл вручную. Развертывание циклов иногда полезно, однако компилятор уже делает это и большую часть времени будет работать лучше, чем вы.
Чтобы включить мою любимую языковую функцию, ее лучше реализовать с помощью шаблонов: простая реализация (возможно, более умная) будет выглядеть так:
// I'm sure the compiler can figure out the inline part, but I'll add it anyways
template<unsigned int N>
inline void print_uint_inner(uint32_t v) {
m_data[m_dataOffset + N] = v - v / 10 * 10 + 48;
print_uint_inner<N-1>(v / 10);
}
// For situations just like this, there's a trick to avoid having to define the base case separately.
inline void print_uint_inner<0>(uint32_t v) {
m_data[m_dataOffset] = v - v / 10 * 10 + 48;
}
template<unsigned int N>
inline void print_uint_helper(uint32_t v) {
print_uint_inner<N-1>(v);
m_dataOffset += N;
}
// We could generate the compile-time binary search with templates too, rather than by hand.
void PrintUint(uint32_t v, char d) {
if (m_dataOffset + 11 > sizeof(m_data)) Flush();
if (v < 100000) {
if (v < 1000) {
if (v < 10) {
print_uint_helper<1>(v);
} else if (v < 100) {
print_uint_helper<2>(v);
} else {
print_uint_helper<3>(v);
}
} else {
if (v < 10000) {
print_uint_helper<4>(v);
} else {
print_uint_helper<5>(v);
}
}
} else {
if (v < 100000000) {
if (v < 1000000) {
print_uint_helper<6>(v);
} else if (v < 10000000) {
print_uint_helper<7>(v);
} else {
print_uint_helper<8>(v);
}
} else {
if (v < 1000000000) {
print_uint_helper<9>(v);
} else {
print_uint_helper<10>(v);
}
}
}
m_data[m_dataOffset++] = d;
}
Делает ли что-то подобное хорошей практике кодирования в целом? Да, но только если удовлетворены все следующие критерии:
- Вы уже написали очевидную, простую для понимания, простую версию.
- Вы профилировали свою программу, так что вы знаете, что этот фрагмент кода стоит достаточно времени, чтобы стоить усилий
- Вы готовы пройти дополнительную работу, чтобы убедиться, что более сложная версия на самом деле верна
- Вы профилировали пересмотренную программу, так что вы знаете, что переписывание фактически улучшило ваше время выполнения.
Кроме того, вам, вероятно, следует сохранить возможность вернуться к простой версии, используя либо константы времени компиляции, либо директивы препроцессора. Это будет важно по двум причинам:
- Когда вы отлаживаете, возможность вернуться к простой версии поможет сузить места, где могут быть проблемы
- Когда вы пытаетесь запустить другой компьютер (или даже тот же компьютер в других условиях), вы можете обнаружить, что сложная версия уже не быстрее простой версии.