Очень плохое повышение:: производительность lexical_cast

Я думаю, что это сделало бы это...

def validIP(address):
    parts = address.split(".")
    if len(parts) != 4:
        return False
    for item in parts:
        if not 0 <= int(item) <= 255:
            return False
    return True
45
задан James McNellis 30 May 2010 в 18:14
поделиться

7 ответов

Edit 2012-04-11

rve совершенно правильно прокомментировал производительность lexical_cast, предоставив ссылку:

http: // www.boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/performance.html

У меня сейчас нет доступа для повышения 1,49, но я помню, как делал мой код быстрее в более старой версии. Итак, я предполагаю:

  1. следующий ответ все еще действителен (хотя бы в целях обучения)
  2. , вероятно, была введена оптимизация где-то между двумя версиями (I ' Я буду искать это)
  3. , что означает, что ускорение все еще становится все лучше и лучше

Исходный ответ

Просто чтобы добавить информацию об отличных ответах Барри и Мотти:

Немного предыстории

Пожалуйста, помните, что Boost написан лучшие разработчики C ++ на этой планете и проверены одними и теми же лучшими разработчиками. Если бы lexical_cast был настолько неправильным, то кто-то взломал бы библиотеку либо критикой, либо кодом.

Я думаю, вы упустили суть реальной ценности lexical_cast ...

Сравнение яблок и апельсинов

В Java вы преобразуете целое число в строку Java. Вы заметите, что я не говорю о массиве символов или определяемой пользователем строке. Заметьте, я не говорю о вашем целочисленном, определяемом пользователем. Я говорю о строгом Java Integer и строгом Java String.

В 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 and Target быть любого типа, который вам нужен, включая встроенные типы, такие как boolean или int , что возможно в 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 , мы могли бы избежать временного внешнего буфера, например).

и напишите удаленно эффективную и правильную 32-разрядную версию. А при некоторой работе это, вероятно, могло бы работать быстрее и безопаснее (если бы у нас был прямой доступ на запись во внутренний буфер std :: string , мы могли бы избежать временного внешнего буфера, например).

и напишите удаленно эффективную и правильную 32-разрядную версию. А при некоторой работе это, вероятно, могло бы работать быстрее и безопаснее (если бы у нас был прямой доступ на запись во внутренний буфер std :: string , мы могли бы избежать временного внешнего буфера, например).

78
ответ дан 26 November 2019 в 20:52
поделиться

Вы можете специализировать lexical_cast для типов int и double . Используйте strtod и strtol в своих специализациях.

namespace boost {
template<>
inline int lexical_cast(const std::string& arg)
{
    char* stop;
    int res = strtol( arg.c_str(), &stop, 10 );
    if ( *stop != 0 ) throw_exception(bad_lexical_cast(typeid(int), typeid(std::string)));
    return res;
}
template<>
inline std::string lexical_cast(const int& arg)
{
    char buffer[65]; // large enough for arg < 2^200
    ltoa( arg, buffer, 10 );
    return std::string( buffer ); // RVO will take place here
}
}//namespace boost

int main(int argc, char* argv[])
{
    std::string str = "22"; // SOME STRING
    int int_str = boost::lexical_cast<int>( str );
    std::string str2 = boost::lexical_cast<std::string>( str_int );

    return 0;
}

Этот вариант будет быстрее, чем при использовании реализации по умолчанию, потому что в реализации по умолчанию существует построение тяжелых потоковых объектов. И это должно быть немного быстрее, чем printf , потому что printf должен анализировать строку формата.

20
ответ дан 26 November 2019 в 20:52
поделиться

lexical_cast является более общим, чем конкретный код, который вы используете в Java и Python. Неудивительно, что общий подход, который работает во многих сценариях (лексическое приведение - это не более чем потоковая передача, а затем обратно во временный поток и обратно), в конечном итоге оказывается медленнее, чем определенные процедуры.

(Кстати, вы можете получить лучшую производительность. вне Java, используя статическую версию, Integer.toString (int) . [1])

Наконец, синтаксический анализ и депарсинг строк обычно не так чувствителен к производительности, если только не пишется компилятор, в этот случай lexical_cast , вероятно, слишком универсален, и при сканировании каждой цифры будут вычисляться целые числа и т. д.

[1] Комментатор "stepancheg" усомнился в моем намеке на то, что статическая версия может дать лучшую производительность. Вот источник, который я использовал:

14
ответ дан 26 November 2019 в 20:52
поделиться

В текущем шаблоне утилиты SDK утечки нет. Как у вас создалось впечатление, что в шаблоне есть утечка? Это просто выделение памяти, которая необходима для представлений.

Утечка - это память, на которую нет указателей и которую больше нельзя освободить.

Вы можете немного уменьшить это, используя вместо этого:

string s = Cast( i );

.

Однако, если производительность действительно важна для вас, вам следует рассмотреть возможность использования другого механизма. Вы можете написать свою собственную версию Cast (), которая (например) создает статический поток строк. Такая версия не будет потокобезопасной, но это может не иметь значения для ваших конкретных потребностей.

Подводя итог, lexical_cast - удобная и полезная функция, но такое удобство приходит (как всегда должно быть) с компромиссами в других областях. .

9
ответ дан 26 November 2019 в 20:52
поделиться

Как сказал Барри, lexical_cast является очень общим, вам следует использовать более конкретную альтернативу, например, проверьте itoa ( int- > строка ) и atoi ( строка -> int ).

2
ответ дан 26 November 2019 в 20:52
поделиться

если скорость важна, или вас просто интересует, насколько быстрыми могут быть такие преобразования с помощью C ++, есть интересующий поток по этому поводу.

Boost.Spirit 2.1 (который должен быть выпущен с Boost 1.40) кажется очень быстрым, даже быстрее, чем эквиваленты C (strtol (), atoi () и т. Д.).

1
ответ дан 26 November 2019 в 20:52
поделиться

К сожалению, у меня пока недостаточно репутации, чтобы прокомментировать ...

lexical_cast в первую очередь медленный, потому что он общий (поиск шаблонов происходит во время компиляции, поэтому вызовы виртуальных функций или другие поиски / разыменования не нужны). lexical_cast , на мой взгляд, медленный, потому что он основан на iostreams C ++, которые в основном предназначены для потоковых операций, а не отдельных преобразований, и потому что lexical_cast должен проверять и преобразовывать ошибки iostream сигналы. Таким образом:

  • объект потока должен быть создан и уничтожен
  • в случае строкового вывода выше, обратите внимание, что компиляторам C ++ трудно избежать копирования буфера (альтернативой является форматирование непосредственно в выходной буфер, например ] sprintf выполняет, хотя sprintf не может безопасно обрабатывать переполнение буфера)
  • lexical_cast должен проверять ошибки stringstream ( ss.fail ( ) ), чтобы генерировать исключения при сбоях преобразования

lexical_cast - это хорошо, потому что исключения (IMO) позволяют отлавливать все ошибки без дополнительных усилий и потому, что у него есть унифицированный прототип. Я лично не понимаю, почему любое из этих свойств требует медленной работы (при отсутствии ошибок преобразования), хотя я не знаю таких быстрых функций C ++ (возможно, Spirit или boost :: xpressive?).

Редактировать: я только что нашел сообщение, в котором упоминается использование BOOST_LEXICAL_CAST_ASSUME_C_LOCALE для включения оптимизации «itoa»: http://old.nabble.com/lexical_cast-optimization-td20817583.html . Также есть ссылка на статью с немного более подробной информацией.

8
ответ дан 26 November 2019 в 20:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: