Редактировать 2012-04-11
rve совершенно справедливо прокомментировал производительность lexical_cast, предоставив ссылку:
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/performance.html
У меня сейчас нет доступа к бустеру 1.49, но я помню, как делал код быстрее на более старой версии. Итак, я думаю:
- следующий ответ остается в силе (если только в целях обучения)
- Возможно, между двумя версиями была введена оптимизация (я поищу)
- , что означает, что буст все лучше и лучше
Оригинальный ответ
Просто чтобы добавить информацию о превосходных ответах Барри и Мотти:
Некоторый фон
Пожалуйста, помните, Boost написан лучшими разработчиками C ++ на этой планете и проверен теми же лучшими разработчиками. Если бы lexical_cast
было так неправильно, кто-то взломал бы библиотеку либо критикой, либо кодом.
Полагаю, вы упустили реальную ценность lexical_cast
...
Сравнение яблок и апельсинов.
В Java вы преобразуете целое число в строку Java. Вы заметите, что я не говорю о массиве символов или пользовательской строке. Вы также заметите, я не говорю о вашем пользовательском целом числе. Я говорю о строгом Java Integer и строгой строке Java.
В Python вы более или менее делаете то же самое.
Как сказано в других публикациях, вы, по сути, используете Java и Python эквиваленты sprintf
(или менее стандартный itoa
).
В C ++ вы используете очень мощное приведение. Не мощный в смысле сырых скоростных характеристик (если вам нужна скорость, возможно, лучше подойдет sprintf
), но мощный в смысле расширяемости.
Сравнение яблок.
Если вы хотите сравнить метод Java Integer.toString
, то вам следует сравнить его с возможностями C sprintf
или C ++ ostream
.
Потоковое решение C ++ будет в 6 раз быстрее (на моем g ++), чем lexical_cast
, и гораздо менее расширяемым:
inline void toString(const int value, std::string & output)
{
// The largest 32-bit integer is 4294967295, that is 10 chars
// On the safe side, add 1 for sign, and 1 for trailing zero
char buffer[12] ;
sprintf(buffer, "%i", value) ;
output = buffer ;
}
Решение C sprintf
будет в 8 раз быстрее (на моем g ++), чем lexical_cast
, но намного менее безопасно:
inline void toString(const int value, char * output)
{
sprintf(output, "%i", value) ;
}
Оба решения работают быстрее или быстрее, чем ваше решение Java (согласно вашим данным).
Сравнение апельсинов.
Если вы хотите сравнить C ++ lexical_cast
, то вам следует сравнить его с этим псевдокодом Java:
Source s ;
Target t = Target.fromString(Source(s).toString()) ;
Source и Target любого типа, включая встроенные типы, такие как boolean
или int
, что возможно в C ++ из-за шаблонов.
Расширяемость? Это грязное слово?
Нет, но это хорошо известно: при написании одним и тем же кодером общие решения конкретных проблем обычно медленнее, чем конкретные решения, написанные для их конкретных проблем.
В текущем случае в наивной точке зрения lexical_cast
будет использовать средства потока для преобразования из типа A
в поток строк, а затем из этого потока строк в тип B
.
Это означает, что до тех пор, пока ваш объект можно выводить в поток и вводить из потока, вы сможете использовать на нем lexical_cast
, не касаясь ни одной строки кода.
Итак, что такое lexical_cast
?
Основное использование лексического литья:
- Простота использования (эй, C ++ приведение, которое работает для всего, что является ценностью!)
- Комбинируя его с тяжелым кодом шаблона, где ваши типы параметризованы, и поэтому вы не хотите иметь дело со спецификой и не хотите знать типы.
- Все еще потенциально относительно эффективен, если у вас есть базовые знания шаблона, как я покажу ниже
Точка 2 здесь очень и очень важна, потому что это означает, что у нас есть один и только один интерфейс / функция для преобразования значения типа в равное или подобное значение другого типа.
Это реальная точка, которую вы упустили, и эта точка стоит с точки зрения производительности.
Но это так неопрятно!
Если вы хотите получить высокую скорость работы, помните, что вы имеете дело с C ++, и что у вас есть множество возможностей для эффективной обработки преобразования, и все же сохраните функцию простоты использования lexical_cast
.
Мне понадобилось несколько минут, чтобы посмотреть на источник lexical_cast и найти жизнеспособное решение. Добавьте в свой код C ++ следующий код:
#ifdef SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT
namespace boost
{
template<>
std::string lexical_cast<std::string, int>(const int &arg)
{
// The largest 32-bit integer is 4294967295, that is 10 chars
// On the safe side, add 1 for sign, and 1 for trailing zero
char buffer[12] ;
sprintf(buffer, "%i", arg) ;
return buffer ;
}
}
#endif
Включив эту специализацию lexical_cast для строк и целых чисел (определив макрос SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT
), мой код стал работать в 5 раз быстрее на моем компиляторе g ++, что означает, что, согласно вашим данным, его производительность должна быть аналогична производительности Java.
И мне понадобилось 10 минут, чтобы взглянуть на буст-код и написать удаленно эффективную и правильную 32-битную версию. И с некоторой работой это, вероятно, могло бы пойти быстрее и безопаснее (если бы у нас был прямой доступ к записи во внутренний буфер std::string
, мы могли бы избежать, например, временного внешнего буфера).