Код с низкой задержкой иногда должен быть «некрасивым»?


21

(Это в основном предназначено для тех, кто обладает специфическими знаниями о системах с низкой задержкой, чтобы люди просто не отвечали безосновательными мнениями).

Считаете ли вы, что существует компромисс между написанием «хорошего» объектно-ориентированного кода и написанием очень быстрого кода с низкой задержкой? Например, избегать виртуальных функций в C ++ / накладных расходов на полиморфизм и т. Д. Переписывать код, который выглядит неприятно, но очень быстро и т. Д.?

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

Мне было бы интересно услышать от людей, которые работали в таких областях.


1
@ user997112: Непосредственная причина очевидна. В нем говорится: «Мы ожидаем, что ответы будут подтверждены фактами, ссылками или конкретными знаниями, но этот вопрос, скорее всего, вызовет дебаты, аргументы, опрос или расширенное обсуждение. Это не обязательно означает, что они правильные, но это было близко причина, выбранная всеми тремя близкими избирателями
Роберт Харви

К счастью, я бы сказал, что причина, по которой этот вопрос привлекает близких избирателей, заключается в том, что он может восприниматься как слабо завуалированная напыщенная речь (хотя я так не думаю).
Роберт Харви

8
Я высовываю свою шею: я выбрал третий голос, чтобы закрыться как "неконструктивный", потому что я думаю, что спрашивающий в значительной степени отвечает на его собственный вопрос. «Красивый» код, который работает недостаточно быстро, чтобы выполнить задание, не соответствует требованию к задержке. «Гадкий» код, который работает достаточно быстро, можно сделать более удобным с помощью хорошей документации. Как вы измеряете красоту или уродство - тема для другого вопроса.
Blrfl

1
Исходный код Disruptor LMAX не слишком уродлив. Есть некоторые «черт возьми» части модели безопасности Java (класс Unsafe) и некоторые аппаратные модификации (переменные с кеш-строкой), но это очень читаемый IMO.
Джеймс

5
@ Carson63000, user1598390 и все, кто еще заинтересован: если вопрос заканчивается закрытием, не стесняйтесь спрашивать о закрытии на нашем Мета-сайте , нет смысла обсуждать закрытие в комментариях, особенно закрытие, которое не произошло . Кроме того, имейте в виду, что каждый закрытый вопрос может быть вновь открыт, это не конец света. За исключением, конечно, если майя были правы, в таком случае было бы хорошо знать вас всех!
Яннис

Ответы:


31

Считаете ли вы, что существует компромисс между написанием «хорошего» объектно-ориентированного кода и написанием кода с очень низкой задержкой?

Да.

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

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


15
«Заставь это работать, тогда сделай это быстро». Этот ответ в значительной степени охватывает все, что я хотел сказать, когда читал вопрос.
Carson63000

13
Я добавлю «Мера, не угадай»
Мартейн Вербург

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

1
В отношении «преждевременной оптимизации» - это все еще применимо, даже если оптимизированный код будет таким же «хорошим», как и неоптимизированный код. Суть в том, чтобы не тратить время на стремление к скорости / тому, чего вам не нужно достигать. На самом деле оптимизация не всегда связана со скоростью, и, возможно, существует такая вещь, как ненужная оптимизация для «красоты». Ваш код не должен быть великим произведением искусства, чтобы быть читаемым и обслуживаемым.
Steve314

Я второй @ Steve314. Я являюсь лидером в производительности продукта и часто нахожу чрезмерно сложный код, происхождение которого я могу проследить до некоторой оптимизации производительности. Упрощение этого кода часто показывает значительное улучшение производительности. Один такой пример превратился в пятикратное улучшение производительности, когда я упростил его (чистое сокращение тысяч строк кода). Очевидно, что никто не потратил время на фактические измерения и просто провел преждевременную оптимизацию того, что, по их мнению, могло быть медленным кодом .
Брэндон

5

Да, пример, который я привожу, не C ++ против Java, но сборка против COBOL, поскольку это то, что я знаю.

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

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

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


3

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

Компромисс происходит от стоимости бизнеса. Для более сложного кода требуются более опытные программисты (и программисты с более сфокусированным набором навыков, например, обладающие архитектурой процессора и знаниями в области проектирования), требуется больше времени для чтения и понимания кода, а также для исправления ошибок. Бизнес-затраты на разработку и поддержку такого кода могут быть в 10–100 раз выше, чем при написании обычного кода.

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

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

Таким образом, владелец бизнеса должен:

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

Эти компромиссы очень специфичны для обстоятельств.

Они не могут быть оптимально решены без участия менеджеров и владельцев продуктов.

Они очень специфичны для платформ. Например, настольные и мобильные процессоры имеют разные соображения. Серверные и клиентские приложения также имеют разные соображения.


Да, как правило, более быстрый код выглядит не так, как обычно написанный код. Любой другой код займет больше времени для чтения. Означает ли это безобразие в глазах смотрящего.

Методы, с которыми я сталкиваюсь, это: (не пытаясь претендовать на какой-либо уровень знаний) оптимизация коротких векторов (SIMD), детализированный параллелизм задач, предварительное выделение памяти и повторное использование объектов.

SIMD обычно оказывает серьезное влияние на низкоуровневую читаемость, хотя обычно для этого не требуются структурные изменения более высокого уровня (при условии, что API разработан с учетом предотвращения узких мест).

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

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

С помощью функциональной (вычислительной) декомпозиции алгоритмы, которые могли бы быть нормально написаны в линейной и концептуально четкой последовательности (строки кода, которые выполняются в том же порядке, в котором они написаны), должны быть разбиты на фрагменты и распределены по нескольким функциям. или классы. (См. «Объективация алгоритма» ниже.) Это изменение сильно помешает коллегам-программистам, которые не знакомы с процессом разработки декомпозиции, который привел к созданию такого кода.

Чтобы сделать такой код поддерживаемым, авторы такого кода должны написать тщательно разработанную документацию алгоритма - далеко за пределами вида комментирования кода или диаграмм UML, сделанных для нормально написанного кода. Это похоже на то, как исследователи пишут свои научные статьи.


Нет, быстрый код не должен противоречить объектно-ориентированности.

Иными словами, можно реализовать очень быстрое программное обеспечение, которое все еще является объектно-ориентированным. Тем не менее, ближе к нижнему концу этой реализации (на уровне «гайки и болты», где происходит большинство вычислений), проектирование объекта может значительно отличаться от проектов, полученных из объектно-ориентированного проектирования (OOD). Проект нижнего уровня направлен на объективизацию алгоритма.

Некоторые преимущества объектно-ориентированного программирования (ООП), такие как инкапсуляция, полиморфизм и композиция, все еще могут быть получены из низкоуровневого алгоритмического объективирования. Это основное обоснование использования ООП на этом уровне.

Большинство преимуществ объектно-ориентированного проектирования (OOD) потеряны. Самое главное, что в дизайне низкого уровня нет интуитивности. Другой программист не может научиться работать с кодом более низкого уровня, не понимая сначала, как алгоритм был преобразован и разложен, и это понимание невозможно получить из полученного кода.


2

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

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


2

Некоторые из исследований, которые я видел, показывают, что очистка легко читаемого кода часто происходит быстрее, чем более сложный и трудно читаемый код. Частично это связано с тем, как оптимизаторы разработаны. Они, как правило, гораздо лучше оптимизируют переменную в регистре, чем делают то же самое с промежуточным результатом вычисления. Длинные последовательности назначений с использованием одного оператора, приводящего к конечному результату, могут быть оптимизированы лучше, чем длинное сложное уравнение. Новые оптимизаторы, возможно, уменьшили разницу между чистым и сложным кодом, но я сомневаюсь, что они устранили его.

Другие оптимизации, такие как развертывание цикла, могут быть добавлены чистым способом, когда это необходимо.

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

Я обнаружил, что правило 80/20 применяется к коду, который я оптимизировал. Как правило, я не оптимизирую ничего, что не занимает как минимум 80% времени. Затем я стремлюсь (и обычно достигаю) 10-кратное увеличение производительности. Это повышает производительность примерно в 4 раза. Большинство оптимизаций, которые я реализовал, не сделали код значительно менее «красивым». Ваш пробег может варьироваться.


2

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

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

Подумайте об основных уклонениях от работы, как вы идете. Сохраните тайные уловки для случаев, когда проблема производительности фактически представляет себя. И если вам действительно нужно написать что-то, что кто-то может понять только благодаря знакомству с конкретной оптимизацией, сделайте все, что вы можете, чтобы хотя бы сделать уродливое легко понятным с точки зрения повторного использования вашей кодовой точки зрения. Код, который работает с жалостью, редко когда-либо делает это, потому что разработчики слишком много думали о том, что следующий человек унаследует, но если частые изменения являются единственной константой приложения (большинство веб-приложений в моем опыте), жесткий / негибкий код, который Трудно изменить, практически попрошайничать о панических беспорядках, чтобы они начали появляться по всей вашей кодовой базе. Чистота и стройность лучше для производительности в долгосрочной перспективе.


Я хотел бы предложить два изменения: (1) Есть места, где требуется скорость. В тех местах, я думаю , что это более целесообразно , чтобы сделать интерфейс легко понять, чем сделать внедрение легко понять, так как последний может быть намного сложнее. (2) «Код, который работает ужасно, редко когда-либо делает это ...», который я хотел бы перефразировать как «Сильный акцент на элегантность и простоту кода редко является причиной плохой производительности. Первый даже более важен, если частые изменения ожидается, ... "
Rwong

Реализация была плохим выбором слов в OOPish разговоре. Я имел ввиду это с точки зрения простоты повторного использования и редактирования. # 2, я только что добавил предложение, чтобы установить, что 2 - это, по сути, точка, которую я высказал.
Эрик Реппен

1

Сложный и уродливый не одно и то же. Код, который имеет много особых случаев, оптимизирован для извлечения всех до последней капли производительности, и который поначалу выглядит как клубок связей и зависимостей, на самом деле может быть очень тщательно спроектирован и довольно красив, как только вы его поймете. Действительно, если производительность (измеряется ли она с точки зрения задержки или чего-то еще) достаточно важна для оправдания очень сложного кода, тогда код должен быть хорошо спроектирован. Если это не так, то вы не можете быть уверены, что вся эта сложность действительно лучше простого решения.

Для меня уродливый код - это небрежный, плохо продуманный и / или излишне сложный код . Я не думаю, что вы захотите какие-либо из этих функций в коде, который должен работать.


1

Считаете ли вы, что существует компромисс между написанием «хорошего» объектно-ориентированного кода и написанием очень быстрого кода с низкой задержкой? Например, избегать виртуальных функций в C ++ / накладных расходов на полиморфизм и т. Д. Переписывать код, который выглядит неприятно, но очень быстро и т. Д.?

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

Тем не менее, проблема заключается в том, что очень многие люди неправильно понимают свои представления о производительности. Новички часто все понимают неправильно, и вся их концептуальная модель «вычислительных затрат» требует доработки, и только алгоритмическая сложность - единственное, что они могут сделать правильно. Промежуточные получают много вещей неправильно. Эксперты ошибаются.

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

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

Проектирование для Производительности

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

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

Грубый дизайн интерфейса

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

Таким образом, объектно-ориентированный дизайн не обязательно должен быть несовместим с проектированием для повышения производительности (с задержкой или пропускной способностью), но в языке, который фокусируется на нем, могут возникнуть соблазны моделировать все более крошечные гранулированные объекты, и там последний оптимизатор не может Помогите. Он не может делать такие вещи, как объединение класса, представляющего одну точку, таким образом, чтобы получить эффективное представление SoA для шаблонов доступа к памяти программного обеспечения. Набор точек с дизайном интерфейса, смоделированным на уровне грубости, предоставляет такую ​​возможность и позволяет итерацию к более и более оптимальным решениям по мере необходимости. Такой дизайн рассчитан на объемную память *.

* Обратите внимание на то, что внимание сосредоточено на памяти, а не на данных , так как работа в критически важных областях в течение длительного времени может привести к изменению вашего взгляда на типы данных и структуры данных и на то, как они подключаются к памяти. Бинарное дерево поиска больше не становится исключительно логарифмической сложностью в таких случаях, как, возможно, несопоставимые и непригодные к кешированию фрагменты памяти для узлов дерева, если этому не помогает фиксированный распределитель. Представление не отклоняет алгоритмическую сложность, но оно видит ее больше независимо от разметки памяти. Также начинают воспринимать итерации работы как об итерациях доступа к памяти. *

Многие критичные к производительности проекты могут быть действительно совместимы с концепцией высокоуровневых конструкций интерфейсов, которые легко понять и использовать людям. Разница заключается в том, что «высокий уровень» в этом контексте будет означать массовую агрегацию памяти, интерфейс, смоделированный для потенциально больших коллекций данных, и с реализацией под капотом, которая может быть довольно низкоуровневой. Визуальной аналогией может быть автомобиль, который действительно удобен, легок в управлении и управлении и очень безопасен при движении со скоростью звука, но если вы наденете капот, внутри останется мало огнедышащих демонов.

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

Пул памяти

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

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

Системы типов, разделяющие память

Одна из трудностей гранулированного объектно-ориентированного проектирования на любом языке состоит в том, что он часто хочет представить множество маленьких пользовательских типов и структур данных. Эти типы могут затем хотеть быть распределенными в маленьких маленьких кусочках, если они распределяются динамически.

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

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

Области, которые требуют детерминистского ответа с минимальной задержкой, без заиканий, - вероятно, это единственное место, где горячие точки не всегда сводятся к одному узкому месту, где крошечные неэффективности могут действительно «накапливаться» (то, что многие люди воображают что происходит неправильно с профилировщиком, чтобы держать их под контролем, но в полях, управляемых задержкой, на самом деле могут быть редкие случаи, когда накапливаются крошечные неэффективности). И многие из наиболее распространенных причин такого накопления могут быть следующие: чрезмерное распределение маленьких кусочков памяти повсюду.

В таких языках, как Java, может быть полезно использовать больше массивов простых старых типов данных, когда это возможно, для узких областей (областей, обрабатываемых в виде замкнутых циклов), таких как массив int(но все еще за громоздким высокоуровневым интерфейсом) вместо, скажем, , ArrayListиз определенных пользователем Integerобъектов. Это позволяет избежать сегрегации памяти, которая обычно сопровождает последнее. В C ++ нам не нужно так сильно ухудшать структуру, если наши шаблоны распределения памяти эффективны, поскольку пользовательские типы могут размещаться там непрерывно и даже в контексте универсального контейнера.

Слияние памяти снова вместе

Решение здесь состоит в том, чтобы найти пользовательский распределитель для однородных типов данных и, возможно, даже для однородных типов данных. Когда крошечные типы данных и структуры данных сглаживаются в битах и ​​байтах в памяти, они приобретают однородный характер (хотя и с некоторыми различными требованиями к выравниванию). Когда мы не смотрим на них с точки зрения памяти, система типов языков программирования «хочет» разделить / разделить потенциально смежные области памяти на маленькие разбросанные кусочки.

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

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

уродливый

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

опасно

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

красота

Тем не менее, все это будет на уровне детализации реализации. Как в ветеране масштабного, так и в критическом отношении к производительности «красота» имеет тенденцию смещаться в сторону дизайна интерфейса, а не деталей реализации. Это становится экспоненциально более высоким приоритетом, чтобы искать «красивые», пригодные для использования, безопасные, эффективные интерфейсы, а не реализации из-за связывания и каскадных разрывов, которые могут произойти перед лицом изменения дизайна интерфейса. Реализации могут быть заменены в любое время. Обычно мы выполняем итерацию к производительности по мере необходимости и как показывают измерения. Ключом к дизайну интерфейса является моделирование на достаточно грубом уровне, чтобы оставить место для таких итераций, не нарушая всю систему.

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

TL; DR

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


0

Не для того, чтобы быть другим, но вот что я делаю:

  1. Напишите это чисто и ремонтопригодно.

  2. Проведите диагностику производительности и исправьте проблемы, о которых вам сообщают, а не те, о которых вы предполагаете Гарантированно, они будут отличаться от того, что вы ожидаете.

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

Так есть ли компромисс? Я так не думаю.


0

Вы можете написать уродливый код, который очень быстр, и вы также можете написать красивый код, который так же быстр, как ваш уродливый код. Узкое место будет заключаться не в красоте / организации / структуре вашего кода, а в выбранных вами методах. Например, вы используете неблокирующие сокеты? Вы используете однопоточный дизайн? Вы используете очередь без блокировки для связи между потоками? Вы производите мусор для GC? Вы выполняете какие-либо блокирующие операции ввода-вывода в критическом потоке? Как видите, это не имеет ничего общего с красотой.


0

Что имеет значение для конечного пользователя?

  • Спектакль
  • Особенности / Функциональность
  • дизайн

Случай 1: оптимизированный плохой код

  • Тяжелое обслуживание
  • Трудно читается, если в качестве проекта с открытым исходным кодом

Случай 2: неоптимизированный хороший код

  • Простое обслуживание
  • Плохой пользовательский опыт

Решение?

Легко оптимизировать критичные для производительности фрагменты кода

например:

Программа состоит из 5 методов , 3 из которых предназначены для управления данными, 1 для чтения с диска, а другой для записи на диск

Эти 3 метода управления данными используют два метода ввода-вывода и зависят от них

Мы бы оптимизировали методы ввода / вывода.

Причина: менее вероятно, что методы ввода-вывода будут изменены, и они не влияют на дизайн приложения, и в целом все в этой программе зависит от них, и, таким образом, они кажутся критичными для производительности, мы использовали бы любой код для их оптимизации ,

Это означает, что мы получаем хороший код и управляемый дизайн программы, сохраняя его быстрым, оптимизируя определенные части кода

Я думаю..

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.