Заставь его работать, сделай его чистым, сделай его твердым, ТОГДА заставь его работать так быстро, как нужно .
Это должно быть нормальным порядком вещей. Ваш самый главный приоритет - сделать что-то, что пройдет приемочные испытания, которые не соответствуют требованиям. Это ваш приоритет, потому что это приоритет вашего клиента; выполнение функциональных требований в сроки разработки. Следующим приоритетом является написание чистого, удобочитаемого кода, который легко понять и который может быть поддержан вашим потомством без каких-либо WTF, когда это станет необходимым (это почти никогда не вопрос «если»; вам или кому-то после вас БУДЕТ идти вернуться и изменить / исправить что-то). Третий приоритет - сделать так, чтобы код придерживался методологии SOLID (или GRASP, если вы предпочитаете), который помещает код в модульные, многоразовые, заменяемые блоки, которые снова помогают в обслуживании (они не только могут понять, что вы сделали и почему, но есть чистые линии, по которым я могу хирургически удалить и заменить фрагменты кода). Последний приоритет - производительность; если код достаточно важен, чтобы он соответствовал спецификациям производительности, он почти наверняка достаточно важен, чтобы его сначала сделали правильным, чистым и твердым.
Повторяя Кристофера (и Дональда Кнута), «преждевременная оптимизация - корень всего зла». Кроме того, тип оптимизации, который вы рассматриваете, является второстепенным (ссылка на ваш новый объект будет создаваться в стеке, независимо от того, даете ли вы ему имя в исходном коде или нет) и типом, который может не вызывать каких-либо различий в скомпилированном. IL. Имена переменных не переносятся в IL, поэтому, так как вы объявляете переменную непосредственно перед ее первым (и, вероятно, только) использованием, я бы поспорил с пивом, что IL идентичен между вашими двумя примерами. Итак, ваш сотрудник на 100% прав; вы ищете не в том месте, если вы ищете именованную переменную против встроенной инстанции для чего-то для оптимизации.
Микро-оптимизации в .NET почти никогда не стоят (я говорю о 99,99% случаев). В C / C ++, возможно, если вы знаете, что делаете. Работая в среде .NET, вы уже достаточно далеко от металлического оборудования, что приводит к значительным накладным расходам при выполнении кода. Итак, учитывая, что вы уже находитесь в среде, которая указывает на то, что вы разочаровались в невероятной скорости, и вместо этого хотите написать «правильный» код, если что-то в среде .NET действительно не работает достаточно быстро, либо его сложность слишком высоко, или вы должны подумать о его распараллеливании. Вот несколько основных указателей для оптимизации; Я гарантирую, что ваша производительность в оптимизации (скорость, полученная за потраченное время) резко возрастет:
- Изменение формы функции имеет большее значение, чем изменение коэффициентов - сложность WRT Big-Oh, вы можете уменьшить количество шагов, которые должны быть выполнены в алгоритме N 2 , вдвое, и у вас все еще есть алгоритм квадратичной сложности, даже если он выполняется в половину времени раньше. Если это нижняя граница сложности для этого типа проблемы, пусть будет так, но если есть решение NlogN, линейное или логарифмическое для той же проблемы, вы выиграете больше, переключая алгоритмы для уменьшения сложности, чем оптимизируя тот, который у вас есть.
- То, что вы не видите сложность, не означает, что она вам не стоит - многие из самых элегантных однострочных слов работают ужасно (например, простое средство проверки регулярных выражений является функцией экспоненциальной сложности, но при этом эффективно простая оценка, включающая деление числа на все простые числа, меньшие, чем его квадратный корень, имеет порядок O (Nlog (sqrt (N))). Linq - отличная библиотека, потому что она упрощает код, но в отличие от механизма SQL, .Net Компилятор не будет пытаться найти наиболее эффективный способ выполнения вашего запроса. Вы должны знать, что произойдет, когда вы используете метод, и, следовательно, почему метод может быть быстрее, если поместить его в цепочку раньше (или позже), производя при этом те же результаты.
- OTOH, почти всегда есть компромисс между сложностью исходного кода и сложностью во время выполнения - SelectionSort очень прост в реализации; Вы могли бы вероятно сделать это в 10LOC или меньше. MergeSort немного сложнее, Quicksort еще сложнее, а RadixSort еще сложнее. Но по мере того, как алгоритмы увеличивают сложность кодирования (и, таким образом, "предварительное" время разработки), они уменьшают сложность времени выполнения; MergeSort и QuickSort - это NlogN, а RadixSort обычно считается линейным (технически это NlogM, где M - наибольшее число в N).
- Прорыв быстро - если есть проверка, которая может быть сделана недорого, что, вероятно, будет правдой и означает, что вы можете двигаться дальше, сделайте эту проверку в первую очередь. Если ваш алгоритм, например, заботится только о числах, заканчивающихся на 1, 2 или 3, наиболее вероятным случаем (учитывая совершенно случайные данные) является число, которое заканчивается какой-то другой цифрой, поэтому проверьте, чтобы число НЕ заканчивалось на 1, 2 или 3, прежде чем делать какие-либо проверки, чтобы увидеть, заканчивается ли число на 1, 2 или 3. Если часть логики требует A & B, и P (A) = 0,9, а P (B) = 0,1, то проверьте B сначала, если только если! A, то! B (как
if(myObject != null && myObject.someProperty == 1)
) или B занимает более чем в 9 раз больше, чем A для оценки ( if(myObject != null && some10SecondMethodReturningBool())
).
- Не задавайте вопросов, на которые вы уже знаете ответ - если у вас есть ряд «провальных» условий, и одно или несколько из этих условий зависят от более простого условия, которое также необходимо проверить, никогда не проверяйте оба это независимо. Например, если у вас есть проверка, которая требует A, и проверка, которая требует A && B, вы должны проверить A, и если true, вы должны проверить B. Если! A, то! A && B, так что даже не беспокойтесь.
- Чем больше раз вы что-то делаете, тем больше вы должны обращать внимание на то, как это делается - это общая тема в разработке, на многих уровнях; в общем смысле разработки: «если обычная задача отнимает много времени или сложна, продолжайте ее выполнять, пока вы не разочарованы и не достаточно осведомлены, чтобы придумать лучший способ». В терминах кода, чем больше раз запускается неэффективный алгоритм, тем больше вы выигрываете в общей производительности, оптимизируя его. Существуют инструменты профилирования, которые могут взять двоичную сборку и ее символы отладки и показать вам, после прохождения некоторых сценариев использования, какие строки кода выполнялись чаще всего. Этим линиям и линиям, которые управляют этими линиями, следует уделять больше всего внимания, потому что любое увеличение эффективности, которое вы достигнете, будет умножено.
- Более сложный алгоритм выглядит как менее сложный алгоритм, если на него бросить достаточно оборудования . В некоторых случаях вам просто нужно осознать, что ваш алгоритм приближается к техническим пределам системы (или ее части), на которой вы ее запускаете; с этого момента, если он должен быть быстрее, вы получите больше, просто запустив его на более качественном оборудовании. Это также относится к распараллеливанию; алгоритм N 2 -сложности при работе на N ядрах выглядит линейным. Итак, если вы уверены, что достигли нижней границы сложности для типа алгоритма, который пишете, ищите способы «разделяй и властвуй».
- Это быстро, когда это достаточно быстро - Если вы не собираете упаковку вручную, чтобы ориентироваться на конкретный чип, всегда есть что-то, что можно получить. Однако, если вы НЕ ХОТИТЕ собирать упаковку вручную, вы всегда должны помнить о том, что клиент назвал бы «достаточно хорошим». Опять же, «преждевременная оптимизация - корень всего зла»; когда ваш клиент звонит достаточно быстро, вы закончите, пока он не будет думать, что это достаточно быстро.