Scala: влияет ли вывод типа переменной на производительность?

в Scala вы можете объявить переменную, указав тип, например: (метод 1)

var x : String = "Hello World"

или вы можете позволить Scala автоматически определять тип переменной (Метод 2)

var x = "Hello World"

почему вы используете метод 1? Есть ли выигрыш в производительности?
И как только переменная была объявлена, будет ли она вести себя точно так же во всех ситуациях, если она была объявлена методом 1 или методом 2?

4 ответов


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

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


У вас не будет никакой разницы в производительности, используя эти два варианта. Они будут скомпилированы в один и тот же код.


другие ответы предполагают, что компилятор вывел то, что вы думаете, что он вывел.

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

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

scala> def build[A, B, C <: Iterable[B]](bs: B*)(implicit cbf: CanBuildFrom[A, B, C]): C = {
     | val b = cbf(); println(b.getClass); b ++= bs; b.result }
build: [A, B, C <: Iterable[B]](bs: B*)(implicit cbf: scala.collection.generic.CanBuildFrom[A,B,C])C

scala> val xs = build(1,2,3)
class scala.collection.immutable.VectorBuilder
xs: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)

scala> val xs: List[Int] = build(1,2,3)
class scala.collection.mutable.ListBuffer
xs: List[Int] = List(1, 2, 3)

scala> val xs: Seq[Int] = build(1,2,3)
class scala.collection.immutable.VectorBuilder
xs: Seq[Int] = Vector(1, 2, 3)

очевидно, это имеет значение для производительности выполнения, если вы получить список или вектор.

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

пример беседы:

https://groups.google.com/forum/#!msg/scala-language/mQ-bIXbC1zs/wgSD4Up5gYMJ

http://grokbase.com/p/gg/scala-user/137mgpjg98/another-funny-quirk

почему Seq.сайту newbuilder возвращая ListBuffer?

https://groups.google.com/forum#!тема/scala-пользователь/1SjYq_qFuKk


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

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

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

val x = new AnyRef { def sayHi() = println("Howdy!") }
x.sayHi

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

warning: reflective access of structural type member method sayHi should be enabled
by making the implicit value scala.language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scala docs for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.

затем вы можете изменить код на этот, который не использует отражение:

trait Talkative extends AnyRef { def sayHi(): Unit }
val x = new Talkative { def sayHi() = println("Howdy!") }
x.sayHi

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

val x: AnyRef = new AnyRef { def sayHi() = println("Howdy!") }
x.sayHi  // ERROR: sayHi is not defined on AnyRef

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

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

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

var shape: Shape = new Circle(1.0)
shape = new Square(1.0)

но в этих случаях нет никакого влияния на производительность.

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