Почему трудно обеспечить эффективность при использовании библиотек?

10

Любая небольшая обработка базы данных может быть легко решена с помощью скриптов Python / Perl / ..., которые используют библиотеки и / или даже утилиты из самого языка. Тем не менее, когда дело доходит до производительности, люди склонны обращаться к языкам C / C ++ / низкого уровня. Кажется, что возможность адаптировать код к потребностям делает эти языки такими привлекательными для BigData - будь то управление памятью, параллелизм, доступ к диску или даже низкоуровневая оптимизация (посредством сборочных конструкций на уровне C / C ++).

Конечно, такой набор преимуществ не обойдется без затрат: написание кода, а иногда и повторное изобретение колеса может быть довольно дорогим / утомительным. Хотя существует множество доступных библиотек, люди склонны сами писать код всякий раз, когда им нужно обеспечить производительность. Что мешает утверждениям производительности использовать библиотеки при обработке больших баз данных?

Например, рассмотрим предприятие, которое непрерывно сканирует веб-страницы и анализирует собранные данные. Для каждого скользящего окна различные извлеченные алгоритмы запускаются на извлеченных данных. Зачем разработчикам отказываться от использования доступных библиотек / фреймворков (будь то сканирование, обработка текста и анализ данных)? Использование уже реализованного материала не только облегчит бремя кодирования всего процесса, но и сэкономит много времени.

В одном кадре :

  • что делает написание кода самостоятельно гарантией производительности?
  • почему рискованно полагаться на фреймворки / библиотеки, когда нужно обеспечить высокую производительность?
Рубенс
источник
1
Вы можете уточнить точный вопрос? Возможно, некоторые возможные ответы также могут помочь.
Амир Али Акбари
@AmirAliAkbari SeanOwen опубликовал ответ, и я заметил отсутствие конкретики в моем вопросе. Я добавил комментарий к его сообщению. Пожалуйста, не стесняйтесь предлагать какие-либо улучшения в сообщении - я планирую удалить его, в противном случае.
Рубенс

Ответы:

4

После того, как я сделал игру переписывания снова и снова (и продолжаю делать это), моей немедленной реакцией была адаптивность .

В то время как фреймворки и библиотеки имеют огромный арсенал (возможно, взаимозаменяемый) подпрограмм для стандартных задач, их свойство фреймворка часто (всегда?) Не позволяет использовать ярлыки. Фактически, большинство сред имеют своего рода базовую инфраструктуру, вокруг которой реализован базовый уровень базовой функциональности. Более специфическая функциональность использует базовый слой и размещается во втором слое вокруг ядра.

Под ярлыками я имею в виду переход от процедуры второго уровня к другой программе второго уровня без использования ядра. Типичным примером (из моего домена) могут быть метки времени: у вас есть источник данных с метками времени. Пока что задача состоит в том, чтобы просто прочитать данные по проводам и передать их ядру, чтобы ваш другой код мог их использовать.

Теперь ваша отрасль по очень хорошей причине меняет формат меток времени по умолчанию (в моем случае они перешли с времени unix на время GPS). Если ваша среда не является отраслевой, очень маловероятно, что они захотят изменить основное представление времени, так что вы в конечном итоге будете использовать среду, которая почти делает то, что вы хотите. Каждый раз, когда вы получаете доступ к своим данным, вы должны сначала конвертировать их в отраслевой формат, а каждый раз, когда вы хотите, чтобы они модифицировались, вы должны конвертировать их обратно в то, что ядро ​​сочтет целесообразным. Невозможно передать данные напрямую из источника в приемник без двойного преобразования.

Это то место, где ваши собственные фреймворки будут блестеть, это всего лишь небольшое изменение, и вы вернетесь к моделированию реального мира, тогда как все другие (не относящиеся к отрасли) фреймворки теперь будут иметь недостаток в производительности.

Со временем расхождение между реальным миром и моделью будет складываться. С рамками вне-полкой вы скоро столкнуться такими вопросами , как: Как я могу представить thisв thatили как рутинном Xпринимать / продукцию Y.

До сих пор речь шла не о C / C ++. Но если по какой-то причине вы не можете изменить структуру, т. Е. Вам приходится мириться с двойным преобразованием данных для перехода от одного конца к другому, то обычно вы используете что-то, что минимизирует дополнительные издержки. В моем случае преобразователь TAI-> UTC или UTC-> TAI лучше всего оставить в необработанном C (или FPGA). Невозможна элегантность, нет глубокой умной структуры данных, которая делает проблему тривиальной. Это просто скучная инструкция переключения, и почему бы не использовать язык, компиляторы которого способны оптимизировать именно это?

hroptatyr
источник
1
+1 Возможно, я виноват в том, что не очень ясно изложил свой пост, поэтому другие не получили его раньше. Это, безусловно, тот ответ, который я искал. Спасибо.
Рубенс
7

Я не думаю, что все тянутся к C / C ++, когда производительность является проблемой.

Преимуществом написания низкоуровневого кода является использование меньшего количества циклов ЦП, а иногда и меньше памяти. Но я бы отметил, что языки более высокого уровня могут обращаться к языкам более низкого уровня и получать некоторые из этих значений. Языки Python и JVM могут сделать это.

Исследователь данных, использующий, например, scikit-learn на своем рабочем столе, уже вызывает сильно оптимизированные нативные подпрограммы для обработки чисел. Нет смысла писать новый код для скорости.

В контексте распределенных «больших данных» вы чаще всего сталкиваетесь с проблемами перемещения данных: передача по сети и ввод-вывод. Родной код не помогает. Помогает не написание одного и того же кода для более быстрой работы, а написание более умного кода.

Языки более высокого уровня позволят вам реализовать более сложные распределенные алгоритмы за определенное время разработки, чем C / C ++. В масштабе, более умный алгоритм с лучшим перемещением данных превзойдет тупой нативный код.

Также обычно верно, что время разработчика и ошибки стоят дороже, чем новое оборудование. Год старшего разработчика может быть полностью загружен на 200 тысяч долларов; в течение года, который также арендует сотни серверов времени вычислений. Может быть, в большинстве случаев просто не имеет смысла тратить время на оптимизацию, тратя на это больше оборудования.

Я не понимаю, что насчет «предоставить», «отключить» и «подтвердить»?

Шон Оуэн
источник
Извините за недопонимание. Я хотел ответить на вопрос о важности контроля над приложением и о том, как этот контроль ослаблен библиотеками. Конечно, вы можете предположить что-то о них (люди обычно не переписывают pthreads), но если данные изменяются (загрузка, пропускная способность, ...), вам может потребоваться доступ к источнику lib для обеспечения производительности. И да, это не обязательно C / C ++ - хотя обычно это языки, выбранные для hpc. Могу ли я удалить свой вопрос или вы хотите изменить его на что-то более конкретное? Я принимаю любые предложения по его улучшению.
Рубенс
1
Нет, это хороший вопрос, вы можете отразить ваши комментарии здесь в изменениях к вопросу, если хотите.
Шон Оуэн
Пожалуйста, проверьте, если вопрос имеет смысл сейчас. Я добавил небольшой кейс, чтобы сделать его более простым. Если вы хотите добавить некоторые соображения в вопрос, пожалуйста, не стесняйтесь редактировать его.
Рубенс
4

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

А ответственность / риски, вытекающие из кода, лежат на плечах разработчиков.

Это небольшой, но я думаю, очень полезный пример из мира .NET ..

Поэтому многие разработчики .NET используют встроенный BinaryReader - BinaryWriter при сериализации своих данных для контроля производительности / получения процесса.

Это исходный код CSharp встроенного в BinaryWriter класса FrameWork, одного из перегруженных методов записи:

// Writes a boolean to this stream. A single byte is written to the stream
// with the value 0 representing false or the value 1 representing true.
// 
public virtual void Write(bool value) 
{
     //_buffer is a byte array which declared in ctor / init codes of the class
    _buffer = ((byte) (value? 1:0));

    //OutStream is the stream instance which BinaryWriter Writes the value(s) into it.
    OutStream.WriteByte(_buffer[0]);
}

Как видите, этот метод может быть написан без дополнительного присваивания переменной _buffer:

public virtual void Write(bool value) 
{
    OutStream.WriteByte((byte) (value ? 1 : 0));
}

Без назначения мы могли бы получить несколько миллисекунд. Эти несколько миллисекунд можно принять как «почти ничего», но что, если есть несколько тысяч записей (то есть в процессе сервера)?

Предположим, что «немногие» равны 2 (миллисекунды), а несколько тысяч экземпляров - только 2.000. Это означает, что на 4 секунды больше времени обработки. Через 4 секунды возвращается.

Если мы продолжим тему из .NET и если вы сможете проверить исходные коды BCL - библиотеки .NET Base Class Library - из MSDN, вы увидите, что разработчик решает, что производительность сильно снижается.

Любой из пунктов из BCL-источника. Обычно вы видите, что разработчик решил использовать циклы while () или foreach (), которые могли бы реализовать более быстрый цикл for () в своем коде.

Этот небольшой выигрыш дает нам общую производительность ..

И если мы вернемся к методу BinaryWriter.Write ()

На самом деле, дополнительное присвоение реализации _buffer не является ошибкой разработчика. Это именно то, что нужно «оставаться в безопасности»!

Предположим, что мы решили не использовать _buffer и решили реализовать второй метод. Если мы попытаемся отправить многотысячные байты по проводам (т. Е. Выгрузить / загрузить данные BLOB или CLOB) с помощью второго метода, это может привести к сбоям, поскольку потеря соединения. Потому что мы пытаемся отправить все данные без каких-либо проверок и механизма контроля. Когда соединение потеряно, и сервер, и клиент никогда не узнают, отправленные данные завершены или нет.

Если разработчик решает «оставаться в безопасности», то обычно это означает, что затраты на производительность зависят от реализованных механизмов «оставаться в безопасности».

Но если разработчик решает «рисковать, повышать производительность», это тоже не ошибка ... До тех пор, пока не начнутся дискуссии о «рискованном» кодировании.

И в качестве небольшого примечания: разработчики коммерческих библиотек всегда стараются оставаться в безопасности, потому что они не могут знать, где будет использоваться их код.

sihirbazzz
источник
4

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

Производительность обычно оценивается во вторичных конкурентных библиотеках. «Библиотека X лучше, потому что она быстрее». Даже тогда очень часто эти библиотеки заменяют наиболее оптимальное решение на то, которое можно широко использовать.

Используя любой фреймворк, вы рискуете существовать более быстрым решением. Я мог бы пойти так далеко, чтобы сказать, что более быстрое решение почти всегда существует.

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

Примером может быть разбор JSON. Существуют сотни библиотек для различных языков, которые превратят JSON в референтный объект и наоборот. Я знаю одну реализацию, которая делает все это в регистрах процессора. Он заметно быстрее, чем все остальные парсеры, но он также очень ограничен, и это ограничение будет зависеть от того, с каким процессором вы работаете.

Является ли хорошей задачей идея создания высокопроизводительного JSON-анализатора для конкретной среды? Я бы использовал уважаемую библиотеку 99 раз из 100. В этом отдельном случае несколько дополнительных циклов ЦП, умноженных на миллион итераций, оправдали бы время разработки.

Стив Каллестад
источник