Стоимость использования параметров в C#

Есть ли у кого-нибудь совет по использованию параметров в С# для передачи аргумента метода. Я подумываю о перегрузке первых 6 аргументов, а затем 7-го, используя функцию params. Мое рассуждение состоит в том, чтобы избежать дополнительного выделения массива, требуемого функцией params. Это для некоторых высокопроизводительных служебных методов. Любой совет? Это пустая трата кода на создание всех перегрузок?


person Carlo V. Dango    schedule 16.10.2010    source источник
comment
Звучит как преждевременная оптимизация для меня...   -  person Petar Minchev    schedule 17.10.2010
comment
Что говорит @Petar. Это ужасно похоже на ненужную микрооптимизацию. Я не знаю С#, но это звучит как что-то, что в любом случае может быть оптимизировано компилятором.   -  person Pekka    schedule 17.10.2010
comment
Возможно, вы могли бы использовать фреймворк в качестве примера и ограничить перегрузки до 3 параметров, а затем использовать параметры для большего количества. например, System.String.Format().   -  person Jeff Mercado    schedule 17.10.2010
comment
Я очень не уверен в том, как компилятор должен оптимизировать выделение массива, поскольку общий механизм заключается в том, что аргументы предоставляются в виде массива, который может повторяться любым способом, который подходит моему коду, пропускать аргументы в определенных позициях и т. д. .   -  person Carlo V. Dango    schedule 17.10.2010
comment
Кто-нибудь делал какие-либо измерения относительно того, сколько% можно получить от перегрузок, а не просто от использования параметров?   -  person Carlo V. Dango    schedule 17.10.2010
comment
почему бы вам не написать простой тест и сравнить только тайминги?   -  person ralf.w.    schedule 17.10.2010
comment
хм, я думаю, что в некоторых случаях массивы (параметры) могут улучшить производительность. Вы отправили только ссылку с методом, а не N*values-of-some-size.   -  person Lasse Espeholt    schedule 17.10.2010
comment
@lasseespholt: в этой ситуации вы можете отправлять только ссылку, но вам нужно создать массив и скопировать в него эти N * значений некоторого размера перед отправкой ссылки.   -  person LukeH    schedule 17.10.2010


Ответы (6)


Честно говоря, меня немного напрягает, что все кричат ​​"преждевременная оптимизация!" Вот почему.

  1. То, что вы говорите, имеет смысл, особенно поскольку вы уже указали, что работаете над высокопроизводительной библиотекой.
  2. Даже классы BCL следуют этому образцу. Рассмотрим все перегрузки string.Format или Console.WriteLine.
  3. Это очень легко сделать правильно. Вся предпосылка, стоящая за движением против преждевременной оптимизации, заключается в том, что когда вы делаете что-то хитрое в целях оптимизации производительности, вы можете случайно что-то сломать и сделать свой код менее ремонтопригодна. Я не вижу в этом опасности; то, что вы делаете, должно быть очень простым как для вас самих, так и для любого будущего разработчика, который может иметь дело с вашим кодом.

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

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

(А тем, кто утверждает, что «компилятор наверняка уже делает что-то подобное» — я бы не был так уверен. Во-первых, если бы это было так, я не понимаю, почему классы BCL следовали бы этому шаблону, поскольку я уже упоминалось. Но что более важно, существует очень большая семантическая разница между методом, который принимает несколько аргументов, и методом, который принимает массив. Просто потому, что один может< /em> используется вместо другого не означает, что компилятор будет или должен попытаться выполнить такую ​​замену).

person Dan Tao    schedule 16.10.2010
comment
Я согласен с тем, что ранняя оптимизация не обязательно означает преждевременную оптимизацию, и похоже, что у ОП, вероятно, есть веские причины для этого, но, не зная больше о том, что происходит внутри этих методов, трудно знать наверняка: acm.org/ubiquity/views/v7i24_fallacy.html bluebytesoftware.com/blog/2010/09/06/ - person LukeH; 17.10.2010
comment
Я не мог бы сказать это лучше. - person Jeff Mercado; 17.10.2010

Да, это стратегия, которую использует платформа .NET. String.Concat() будет хорошим примером. Он имеет перегрузки до 4 строк, а также резервную версию, которая принимает строку параметров []. Здесь довольно важно то, что Concat должен быть быстрым и помочь пользователю упасть в яму успеха, когда он использует оператор + вместо StringBuilder.

Дублирование кода, которое вы получите, — это цена. Вы бы профилировали их, чтобы увидеть, стоит ли ускорение головной боли обслуживания.

Fwiw: в среде .NET существует множество подобных микрооптимизаций. В некоторой степени необходимо, потому что дизайнеры не могли предсказать, как будут использоваться их классы. String.Concat() с такой же вероятностью будет использоваться в тесном внутреннем цикле, который имеет решающее значение для производительности программы, как, скажем, считыватель конфигурации, который запускается только один раз при запуске. Как конечный пользователь собственного кода вы обычно можете позволить себе не беспокоиться об этом. Верно и обратное: код платформы .NET на удивление свободен от микрооптимизаций, когда маловероятно, что их выгода будет измерима. Например, предоставление перегрузок, когда основной код все равно работает медленно.

person Hans Passant    schedule 16.10.2010
comment
Ха-ха, упасть в яму успеха — мне это нравится. - person Dan Tao; 17.10.2010

Вы всегда можете передать Tuple в качестве параметра или, если типы параметров всегда одинаковы, IList<T>.

Как уже говорилось в других ответах и ​​комментариях, вы должны оптимизировать только после:

  1. Обеспечение правильного поведения.
  2. Определение необходимости оптимизации.
person Oded    schedule 16.10.2010
comment
Кортеж также потребует дополнительного выделения памяти, как и массив. Почему было бы лучше? - person Carlo V. Dango; 18.05.2014

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

Например, если вы передаете параметры в Console.WriteLine, там также создается скрытый массив, так что в любом случае у вас будет массив.

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

person Iravanchi    schedule 16.10.2010
comment
Да, это хороший момент. Контекст, в котором я задал вопрос, на самом деле заключался в том, что я могу выразить значимую функцию, когда предоставлен один или несколько аргументов. - person Carlo V. Dango; 17.10.2010

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

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

person egrunin    schedule 16.10.2010

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

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

person Rei Miyasaka    schedule 16.10.2010