Подтвердить что ты не робот

Эмпирические данные о последствиях неизменности?

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

  • Безопасность (особенно с резьбой)
  • Сокращенное количество объектов
  • Позволяет определенные оптимизации (особенно для сборщика мусора)

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

Итак, мой вопрос: проводились ли какие-либо статистические исследования о влиянии неизменяемости в реальном коде?

4b9b3361

Ответ 1

Я бы указал на Пункт 15 в Эффективной Java. Значение неизменяемости в дизайне (и это не всегда уместно - это просто хорошее первое приближение), а предпочтения дизайна редко аргументируются со статистической точки зрения, но мы видели изменчивые объекты (Calendar, Date), которые пошло очень плохо, и серьезные замены (JodaTime, JSR-310) выбрали неизменность.

Ответ 2

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

Подробнее см. эту страницу.

Ответ 3

Итак, мой вопрос: есть ли любые статистические исследования, проведенные на последствия неизменности в реальном мире код?

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

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

В чем его ценность, способность компилятора оптимизировать неизменяемые объекты хорошо документирована. Сверху моей головы:

  • Компилятор Haskell выполняет обезлесение (также называемое short-cut fusion), где Haskell преобразует выражение map f . map g в map f . g, Поскольку функции Haskell неизменяемы, эти выражения гарантируют получение эквивалентного вывода, но вторая функция выполняется в два раза быстрее, так как нам не нужно создавать промежуточный список.
  • Исключение общего подвыражения, где мы могли бы преобразовать x = foo(12); y = foo(12) в temp = foo(12); x = temp; y = temp;, возможно только в том случае, если компилятор может гарантировать, что foo является чистой функцией. Насколько мне известно, D-компилятор может выполнять такие подстановки, используя ключевые слова pure и immutable. Если я правильно помню, некоторые компиляторы C и С++ будут агрессивно оптимизировать обращения к этим функциям с пометкой "чистый" (или независимо от эквивалентного ключевого слова).
  • До тех пор, пока у нас нет изменяемого состояния, достаточно интеллектуальный компилятор может выполнять линейные блоки кода с несколькими потоками с гарантией того, что мы не испортим состояние переменных в другом потоке.

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


Конечно, это все анекдотические доказательства, но это в значительной степени лучшее, что вы получите. Неизменяемые vs mutable debate - это, в основном, pissing match, и вы не найдете бумагу, предлагающую широкое обобщение, такое как "функциональное программирование превосходит обязательное программирование".

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

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

Ответ 4

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

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

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

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

Ответ 5

  • Не спорьте с проф. Вам нечего выиграть.
  • Это открытые вопросы, такие как динамическая и статическая типизация. Мы иногда считаем, что функциональные методы, содержащие неизменные данные, лучше по разным причинам, но в основном это вопрос стиля.

Ответ 6

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

Ответ 7

Неизменность - хорошая вещь для объектов ценности. Но как насчет других вещей? Представьте объект, который создает статистику:

Stats s = new Stats ();

... some loop ...
     s.count ();

s.end ();
s.print ();

который должен печатать "Обработано 536.21 строк/с". Как вы планируете реализовать count() с неизменяемым? Даже если вы используете объект неизменяемого значения для самого счетчика, s не может быть неизменным, поскольку он должен будет заменить объект-счетчик внутри себя. Единственный выход:

     s = s.count ();

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

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

Ответ 8

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

Между тем очень легко сделать убедительные аргументы в пользу неизменности, по крайней мере, на Java и, возможно, на языках С# и других языках OO. Как утверждает Ишай, Effective Java хорошо разбирается в этом аргументе. То же самое делает копия Java Concurrency в Практике, сидящей на моей книжной полке.

Ответ 9

Неизменяемые объекты позволяют использовать код, который может совместно использовать значение объекта путем совместного использования ссылки. Вместе с тем, в Mutable объектах есть идентификатор кода, который хочет поделиться идентификатором объекта, чтобы сделать это, разделив ссылку. Оба варианта совместного использования важны для большинства приложений. Если у вас нет неизменяемых объектов, возможно обмен значениями путем их копирования в новые объекты или объекты, предоставленные предполагаемым получателем этих значений. Получение моего без изменяемых объектов намного сложнее. Можно несколько "подделать" изменчивые объекты, сказав stateOfUniverse = stateOfUniverse.withSomeChange(...), но потребовало бы, чтобы ничто не изменяло stateOfUniverse, пока его метод withSomeChange работает [исключая любую разновидность многопоточности]. Кроме того, если таковой был, например, пытаясь отследить парк грузовиков, а часть кода заинтересована в одном конкретном грузовике, необходимо, чтобы этот код всегда искал этот грузовик в таблице грузовиков в любое время, когда он мог измениться.

Лучший подход заключается в разделении юниверса на сущности и значения. Объекты имели бы изменяемые характеристики, но неизменяемую идентичность, поэтому место хранения, например, тип Truck может продолжать идентифицировать один и тот же грузовик, даже если сам грузовик меняет положение, загружает и выгружает груз и т.д. Значения не имели бы обычно определенной личности, но имели бы неизменные характеристики. A Truck может сохранить свое местоположение как тип WorldCoordinate. A WorldCoordinate, который представляет 45.6789012N 98.7654321W, будет продолжаться до тех пор, пока существует какая-либо ссылка на него; если бы грузовик, который находился в этом месте, немного сдвинулся на север, он создаст новый WorldCoordinate для представления 45.6789013N 98.7654321W, оставит старый и сохранит ссылку на этот новый.

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

ImmutableMapping<int,Truck> trucks = stateOfUniverse.getTrucks();
Truck myTruck = trucks.get(myTruckId);
myTruck = myTruck.withLocation(newLocation);
trucks = trucks.withItem(myTruckId,myTruck);
stateOfUniverse = stateOfUniverse.withTrucks(trucks);

но рассуждение об этом коде будет более сложным, чем было бы:

myTruck.setLocation(newLocation);