Я пишу код, который принимает число от пользователя и печатает обратно в виде букв в виде строки. Я хочу знать, что лучше с точки зрения производительности, чтобы иметь такие заявления, как
if (n < 100) {
// code for 2-digit numbers
} else if (n < 1000) {
// code for 3-digit numbers
} // etc..
или поместить число в строку и получить его длину, а затем работать с ним как со строкой.
Код написан на C ++.
Конечно if-else
будет быстрее
Чтобы сравнить два числа, вы просто сравниваете их побитово (есть разные способы сделать это, но это очень быстрая операция).
Чтобы получить длину строки, вам нужно будет создать строку, поместить в нее данные и каким-то образом вычислить длину (могут быть разные способы сделать это, самый простой — подсчитать все символы). Конечно, это занимает гораздо больше времени.
На простом примере, хотя вы не заметите никакой разницы. Меня часто поражает, что люди занимаются такими вещами (без обид). Это не будет иметь никакого значения для вас, если код будет выполняться в 0.003
секунд вместо 0.001
секунды действительно … Такие низкоуровневые оптимизации следует выполнять только после того, как вы знать что именно это место является узким местом вашего приложения, и когда вы конечно что вы можете увеличить производительность на приличную сумму.
Пока вы не измеряете, и это действительно узкое место, не беспокойтесь о производительности.
Тем не менее, следующее должно быть еще быстрее (для удобства чтения предположим, что вы используете тип в диапазоне от 0 до 99999999):
if (n < 10000) {
// code for less or equal to 4 digits
if (n < 100)
{
//code for less or equal to 2 digits
if (n < 10)
return 1;
else
return 2;
}
else
{
//code for over 2 digits, but under or equal to 4
if (n>=1000)
return 4;
else
return 3;
}
} else {
//similar
} // etc..
По сути, это разновидность бинарного поиска. В худшем случае это займет O(log(n))
в отличие от O(n)
— n
будучи максимальным количеством цифр.
string
вариант будет медленнее:
std::stringstream ss; // allocation, initialization ...
ss << 4711; // parsing, setting internal flags, ...
std::string str = ss.str(); // allocations, array copies ...
// cleaning up (compiler does it for you) ...
str.~string();
ss.~stringstream(); // destruction ...
...
указывают, что происходит больше вещей.
Компактный (хороший для кеша) цикл (хороший для предсказания ветвлений) может быть тем, что вам нужно:
int num_digits (int value, int base=10) {
int num = 0;
while (value) {
value /= base;
++num;
}
return num;
}
int num_zeros (int value, int base=10) {
return num_decimal_digits(value, base) - 1;
}
В зависимости от обстоятельств, поскольку он удобен для кэширования и прогнозирования, это может быть быстрее, чем решения, основанные на реляционных операторах.
Шаблонный вариант позволяет компилятору выполнить некоторые микрооптимизации для вашего подразделения:
template <int base=10>
int num_digits (int value) {
int num = 0;
while (value) {
value /= base;
++num;
}
return num;
}
Ответы хорошие, но подумайте немного об относительном времени.
Даже самый медленный метод, который вы можете себе представить, программа может сделать это за доли секунды, например, около 100 микросекунд.
Сравним это с самым быстрым пользователем, которого вы можете себе представить, который мог набрать число, может быть, за 500 миллисекунд, и кто мог прочитать результат еще через 500 миллисекунд, прежде чем делать что-либо следующее.
Ладно, машина практически ничего не делает в течение 1000 миллисекунд, а в середине она должна хрустеть, как сумасшедшая, в течение 100 микросекунд, потому что, в конце концов, мы не хотим, чтобы пользователь думал, что программа работает медленно 😉