Ответ 1
Мне также пришлось оптимизировать много кода Scala в прошлом. Следующее не должно быть полным списком, всего несколько практических замечаний, которые могут вам помочь:
-
Да, замена цикла
for
наwhile
выполняется быстрее, даже с Scala 2.10. Подробнее об этом см. связанный разговор в комментариях. Кроме того, имейте в виду, что использование "для фильтрации" (условие, выполняемое после повторного набора) приведет к коробке/распаковке вашего состояния, что может иметь большое влияние на производительность (см. это сообщение для подробностей). -
На вопрос об неизменяемом vs. mutable просто отвечает количество обновлений, которые вы должны выполнить, и мне сложно дать общий ответ.
-
До сих пор я не наблюдал значительных различий в производительности между 2.8, 2.9 и 2.10. Но ясно, что это зависит от проблемы. Например, если ваш алгоритм сильно использует Range.sum, вы увидите большие различия (потому что теперь это O (1) в 2.10).
-
Я заметил, что использование соответствующей Java-коллекции вместо версии Scala также может привести к значительным ускорениям (в качестве показателя шара можно сказать порядка 5-10%). Например, у меня были следующие результаты (отображаемые время выполнения) в микрообъекте для очень специфической проблемы (примечание: не обобщайте из этого, запустите свой собственный).
ColtBitVector min: 0.042 avg: 0.245 max: 40.120 JavaBitSet min: 0.043 avg: 0.165 max: 4.306 JavaHashSet min: 0.191 avg: 0.716 max: 12.624 JavaTreeSet min: 0.313 avg: 1.428 max: 64.504 ScalaBitSetImmutable min: 0.380 avg: 1.675 max: 13.838 ScalaBitSetMutable min: 0.423 avg: 3.693 max: 457.146 ScalaSetImmutable min: 0.458 avg: 2.305 max: 9.998 ScalaSetMutable min: 0.340 avg: 1.332 max: 10.974
Проблема заключалась в том, чтобы вычислить простое пересечение целых множеств (с очень конкретным размером и количеством множеств). То, что я хочу продемонстрировать: выбор правильной/неправильной коллекции может оказать значительное влияние! Опять же, я думаю, что сложно дать общий совет, какой из этих типов данных выбрать, поскольку это говорит только о производительности в этой специальной проблеме пересечения (но я выбрал Java
HashSet
в нескольких случаях по сравнению с альтернативами). Также обратите внимание, что эта проблема пересечения не требует изменяемого типа данных. Тем не менее, могут быть различия в производительности даже в неизменяемой функциональности (и в то время как в отношенииSet
это была изменчивая коллекция, которая была значительно быстрее, она является неизменной дляBitSet
). Таким образом, в зависимости от ситуации вы можете захотеть выбрать изменяемую коллекцию по неизменному для максимальной производительности (используйте с осторожностью!). -
Мне сказали, что объявление переменной
private[this] var foo = ...
предотвращает создание функций getter/setter и должно быть быстрее (отказ от ответственности: я никогда не подтверждал это в microbenchmark). -
При работе с общими типами определение версии
@specialized
для определенных типов должно привести к ускорению. -
Хотя я стараюсь избегать обобщений, я мог бы жить со следующим: Попробуйте использовать собственные массивы. Во многих моих тестах я просто использовал Массивы, что имеет смысл, учитывая их реализацию в JVM.
-
Небольшой момент, который приходит мне на ум: я наблюдал различия в построении коллекций либо
origCollection.toSomeCollectionName
над ручным построением и построением с использованием сопутствующего объекта (т.е.SomeCollectionName(origCollection :_*)
). Во многих случаях последний был значительно быстрее.