[SCALA] 중첩 된 방법의 비용
SCALA중첩 된 방법의 비용
스칼라에서 하나의 다른 방법 안에 방법을 정의 할 수 있습니다. 이 정의 블록의 내부에 사용 자신의 범위를 제한한다. 나는 몇 고차 함수를 사용하는 코드의 가독성을 향상시키기 위해 그들을 사용합니다. 익명 함수 리터럴과는 달리,이 날을에 전달하기 전에 그들에게 의미있는 이름을 부여 할 수 있습니다.
예를 들면 :
class AggregatedPerson extends HashSet[PersonRecord] {
def mostFrequentName: String = {
type NameCount = (String, Int)
def moreFirst(a: NameCount, b: NameCount) = a._2 > b._2
def countOccurrences(nameGroup: (String, List[PersonRecord])) =
(nameGroup._1, nameGroup._2.size)
iterator.toList.groupBy(_.fullName).
map(countOccurrences).iterator.toList.
sortWith(moreFirst).head._1
}
}
어떤 런타임 비용 때문에 나는 알고 있어야 중첩 된 메소드 정의의가 있습니까?
대답은 폐쇄에 대한 차이가 있습니까?
해결법
-
==============================
1.compilaton 동안 중첩 기능과 MoveFirst를 countOccurences mostFrequentName가 동일한 레벨로 밖으로 이동한다. MoveFirst를 $ 1 countOccurences $ 1 : 그들은 컴파일러 합성 이름을 얻는다.
compilaton 동안 중첩 기능과 MoveFirst를 countOccurences mostFrequentName가 동일한 레벨로 밖으로 이동한다. MoveFirst를 $ 1 countOccurences $ 1 : 그들은 컴파일러 합성 이름을 얻는다.
당신이 인수리스트를 사용하지 말고, 다음 방법 중 하나를 참조 할 때 또한, 그것은 함수에 올려진다. 그래서 (countOccurences)를지도하는지도를 작성하는 것과 동일 ((A : (문자열, 목록 [PersonRecord])) => countOccurences의 (a)). 이 익명 함수는 더 앞으로 countOccurences의 $보다 아무것도하지 않는 별도의 클래스 AggregatedPerson $$ anonfun $ mostFrequentName $ 2로 컴파일됩니다.
사이드 참고로하는 기능에있어서의 해제 공정은 에타 확장 불린다. 그것은 당신이 (당신의 예에서와 같이) 함수 유형이 예상되는 상황에서 인수 목록을 생략하면 트리거, 또는 당신이 사용하는 경우 _ 전체 인수 목록 대신에, 또는 각 인수 대신에 (발 F1 = countOccurences _한다 ; 발 F2 = countOccurences (_).
코드가 폐쇄에 직접 있었다면, 당신은 당신의 스택 한 적은 메서드 호출 및 생성 한 적은 합성 방법을 것이다. 이것의 성능에 미치는 영향은 대부분의 경우 제로 될 가능성이 높습니다.
나는 구조 코드에 대한 내용 인 유용한 도구로 찾아 귀하의 예를 매우 관용적 스칼라을 고려하십시오.
또 다른 유용한 도구는 발을 초기화 작은 블록을 사용하고 있습니다 :
val a = { val temp1, temp2 = ... f(temp1, temp2) }
당신은 스칼라 코드가 JVM에 대한 준비 형태로 변환되는 방법을 정확하게 볼 수 scalac의 -print를 사용할 수 있습니다. Heres는 프로그램의 출력 :
[[syntax trees at end of cleanup]]// Scala source: nested-method.scala package <empty> { class AggregatedPerson extends scala.collection.mutable.HashSet with ScalaObject { def mostFrequentName(): java.lang.String = AggregatedPerson.this.iterator().toList().groupBy({ (new AggregatedPerson$$anonfun$mostFrequentName$1(AggregatedPerson.this): Function1) }).map({ { (new AggregatedPerson$$anonfun$mostFrequentName$2(AggregatedPerson.this): Function1) } }, collection.this.Map.canBuildFrom()).$asInstanceOf[scala.collection.MapLike]().iterator().toList().sortWith({ { (new AggregatedPerson$$anonfun$mostFrequentName$3(AggregatedPerson.this): Function2) } }).$asInstanceOf[scala.collection.IterableLike]().head().$asInstanceOf[Tuple2]()._1().$asInstanceOf[java.lang.String](); final def moreFirst$1(a: Tuple2, b: Tuple2): Boolean = scala.Int.unbox(a._2()).>(scala.Int.unbox(b._2())); final def countOccurrences$1(nameGroup: Tuple2): Tuple2 = new Tuple2(nameGroup._1(), scala.Int.box(nameGroup._2().$asInstanceOf[scala.collection.SeqLike]().size())); def this(): AggregatedPerson = { AggregatedPerson.super.this(); () } }; @SerialVersionUID(0) @serializable final <synthetic> class AggregatedPerson$$anonfun$mostFrequentName$1 extends scala.runtime.AbstractFunction1 { final def apply(x$1: PersonRecord): java.lang.String = x$1.fullName(); final <bridge> def apply(v1: java.lang.Object): java.lang.Object = AggregatedPerson$$anonfun$mostFrequentName$1.this.apply(v1.$asInstanceOf[PersonRecord]()); def this($outer: AggregatedPerson): AggregatedPerson$$anonfun$mostFrequentName$1 = { AggregatedPerson$$anonfun$mostFrequentName$1.super.this(); () } }; @SerialVersionUID(0) @serializable final <synthetic> class AggregatedPerson$$anonfun$mostFrequentName$2 extends scala.runtime.AbstractFunction1 { final def apply(nameGroup: Tuple2): Tuple2 = AggregatedPerson$$anonfun$mostFrequentName$2.this.$outer.countOccurrences$1(nameGroup); <synthetic> <paramaccessor> private[this] val $outer: AggregatedPerson = _; final <bridge> def apply(v1: java.lang.Object): java.lang.Object = AggregatedPerson$$anonfun$mostFrequentName$2.this.apply(v1.$asInstanceOf[Tuple2]()); def this($outer: AggregatedPerson): AggregatedPerson$$anonfun$mostFrequentName$2 = { if ($outer.eq(null)) throw new java.lang.NullPointerException() else AggregatedPerson$$anonfun$mostFrequentName$2.this.$outer = $outer; AggregatedPerson$$anonfun$mostFrequentName$2.super.this(); () } }; @SerialVersionUID(0) @serializable final <synthetic> class AggregatedPerson$$anonfun$mostFrequentName$3 extends scala.runtime.AbstractFunction2 { final def apply(a: Tuple2, b: Tuple2): Boolean = AggregatedPerson$$anonfun$mostFrequentName$3.this.$outer.moreFirst$1(a, b); <synthetic> <paramaccessor> private[this] val $outer: AggregatedPerson = _; final <bridge> def apply(v1: java.lang.Object, v2: java.lang.Object): java.lang.Object = scala.Boolean.box(AggregatedPerson$$anonfun$mostFrequentName$3.this.apply(v1.$asInstanceOf[Tuple2](), v2.$asInstanceOf[Tuple2]())); def this($outer: AggregatedPerson): AggregatedPerson$$anonfun$mostFrequentName$3 = { if ($outer.eq(null)) throw new java.lang.NullPointerException() else AggregatedPerson$$anonfun$mostFrequentName$3.this.$outer = $outer; AggregatedPerson$$anonfun$mostFrequentName$3.super.this(); () } } }
-
==============================
2.작은 런타임 비용이있다. 당신은 여기 (긴 코드에 대한 사과)을 관찰 할 수있다 :
작은 런타임 비용이있다. 당신은 여기 (긴 코드에 대한 사과)을 관찰 할 수있다 :
object NestBench { def countRaw() = { var sum = 0 var i = 0 while (i<1000) { sum += i i += 1 var j = 0 while (j<1000) { sum += j j += 1 var k = 0 while (k<1000) { sum += k k += 1 sum += 1 } } } sum } def countClosure() = { var sum = 0 var i = 0 def sumI { sum += i i += 1 var j = 0 def sumJ { sum += j j += 1 var k = 0 def sumK { def sumL { sum += 1 } sum += k k += 1 sumL } while (k<1000) sumK } while (j<1000) sumJ } while (i<1000) sumI sum } def countInner() = { var sum = 0 def whileI = { def whileJ = { def whileK = { def whileL() = 1 var ksum = 0 var k = 0 while (k<1000) { ksum += k; k += 1; ksum += whileL } ksum } var jsum = 0 var j = 0 while (j<1000) { jsum += j; j += 1 jsum += whileK } jsum } var isum = 0 var i = 0 while (i<1000) { isum += i; i += 1 isum += whileJ } isum } whileI } def countFunc() = { def summer(f: => Int)() = { var sum = 0 var i = 0 while (i<1000) { sum += i; i += 1 sum += f } sum } summer( summer( summer(1) ) )() } def nsPerIteration(f:() => Int): (Int,Double) = { val t0 = System.nanoTime val result = f() val t1 = System.nanoTime (result , (t1-t0)*1e-9) } def main(args: Array[String]) { for (i <- 1 to 5) { val fns = List(countRaw _, countClosure _, countInner _, countFunc _) val labels = List("raw","closure","inner","func") val results = (fns zip labels) foreach (fl => { val x = nsPerIteration( fl._1 ) printf("Method %8s produced %d; time/it = %.3f ns\n",fl._2,x._1,x._2) }) } } }
정수를 합산위한 네 가지 방법이 있습니다 :
그리고 우리는 내부 루프에서 촬영 나노초의 관점에서 내 컴퓨터에 결과를 참조하십시오
scala> NestBench.main(Array[String]()) Method raw produced -1511174132; time/it = 0.422 ns Method closure produced -1511174132; time/it = 2.376 ns Method inner produced -1511174132; time/it = 0.402 ns Method func produced -1511174132; time/it = 0.836 ns Method raw produced -1511174132; time/it = 0.418 ns Method closure produced -1511174132; time/it = 2.410 ns Method inner produced -1511174132; time/it = 0.399 ns Method func produced -1511174132; time/it = 0.813 ns Method raw produced -1511174132; time/it = 0.411 ns Method closure produced -1511174132; time/it = 2.372 ns Method inner produced -1511174132; time/it = 0.399 ns Method func produced -1511174132; time/it = 0.813 ns Method raw produced -1511174132; time/it = 0.411 ns Method closure produced -1511174132; time/it = 2.370 ns Method inner produced -1511174132; time/it = 0.399 ns Method func produced -1511174132; time/it = 0.815 ns Method raw produced -1511174132; time/it = 0.412 ns Method closure produced -1511174132; time/it = 2.357 ns Method inner produced -1511174132; time/it = 0.400 ns Method func produced -1511174132; time/it = 0.817 ns
그래서, 결론은 다음과 같습니다 중첩 기능은 정말 간단 경우 모두에서 당신을 다치게하지 않습니다 - JVM은 호출 (따라서 원료 및 내부주고 같은 시간) 인라인 될 수 있다는 것을 알아낼 것이다. 당신은 더 많은 기능 접근을 경우,이 함수의 호출은 완전히 무시 될 수는 없지만, 소요되는 시간은 하고서 (추가 통화 당 약 0.4 NS)입니다. 당신은 폐쇄를 많이 사용하는 경우, 그들이 하나의 가변 변수에 기록이 경우 적어도 통화 당 1 NS 같은의 오버 헤드를 제공 닫는.
당신은 다른 질문에 대한 답을 찾을 수 위의 코드를 수정할 수 있지만 결론은 "아무런 처벌"단지 걱정 "까지 사이에 이르기까지 매우 빠르고, 모든 점이다 그렇지 않으면 최소한의 일이 매우 협소 한 내부 루프에 대한 해야 할 것".
(추신 비교를 위해, 단일의 작은 객체의 생성 내 머신 ~ 4 NS 걸린다.)
-
==============================
3.년 1 월 2014 년 현재
년 1 월 2014 년 현재
현재 벤치 마크는 ~ 3 세이며 핫스팟 컴파일러는 크게 진화했다. 나는 또한 벤치 마크를 수행하기 위해 구글 캘리퍼스를 사용하고 있습니다.
import com.google.caliper.SimpleBenchmark class Benchmark extends SimpleBenchmark { def timeRaw(reps: Int) = { var i = 0 var result = 0L while (i < reps) { result += 0xc37e ^ (i * 0xd5f3) i = i + 1 } result } def normal(i: Int): Long = 0xc37e ^ (i * 0xd5f3) def timeNormal(reps: Int) = { var i = 0 var result = 0L while (i < reps) { result += normal(i) i = i + 1 } result } def timeInner(reps: Int) = { def inner(i: Int): Long = 0xc37e ^ (i * 0xd5f3) var i = 0 var result = 0L while (i < reps) { result += inner(i) i = i + 1 } result } def timeClosure(reps: Int) = { var i = 0 var result = 0L val closure = () => result += 0xc37e ^ (i * 0xd5f3) while (i < reps) { closure() i = i + 1 } result } def normal(i: Int, j: Int, k: Int, l: Int): Long = i ^ j ^ k ^ l def timeUnboxed(reps: Int) = { var i = 0 var result = 0L while (i < reps) { result += normal(i,i,i,i) i = i + 1 } result } val closure = (i: Int, j: Int, k: Int, l: Int) => (i ^ j ^ k ^ l).toLong def timeBoxed(reps: Int) = { var i = 0 var result = 0L while (i < reps) { closure(i,i,i,i) i = i + 1 } result } }
benchmark ns linear runtime Normal 0.576 = Raw 0.576 = Inner 0.576 = Closure 0.532 = Unboxed 0.893 = Boxed 15.210 ==============================
무엇 매우 놀라운 것은 그 폐쇄 시험은 빨리 다른 사람보다가 4Ns에 대해 완성 된 것입니다. 이 핫스팟의 특질 대신 실행 환경 것 같다, 다수의 실행이 같은 추세를 돌아왔다.
수행 권투는 큰 성능 저하는 것을 클로저를 사용하면, 약 기본 수학 연산을 많이 할 정도로 언 박싱 및 reboxing 하나를 수행 할 3.579ns 걸립니다. 이 특정 위치에서 가지 작업이 새로운 최적화에서 수행되고 더 잘 얻을 수 있습니다. 일반적인 경우, 권투 miniboxing에 의해 완화 될 수 있습니다.
편집하다: 정말 여기에 도움이되지 않는 새로운 최적화, 그것은 0.1 ns의 빠른 0.1 ns의 느린 폐쇄을하고 박스형 :
benchmark ns linear runtime Raw 0.574 = Normal 0.576 = Inner 0.575 = Closure 0.645 = Unboxed 0.889 = Boxed 15.107 ==============================
magarciaEPFL / 스칼라에서 2.11.0-20131209-220002-9587726041 수행
java version "1.7.0_51" Java(TM) SE Runtime Environment (build 1.7.0_51-b13) Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)
Scala compiler version 2.10.3 -- Copyright 2002-2013, LAMP/EPFL
from https://stackoverflow.com/questions/2482326/the-cost-of-nested-methods by cc-by-sa and MIT license
'SCALA' 카테고리의 다른 글
[SCALA] (22 개) 필드는 2.11 + 프레임 워크 2.3 케이스 클래스와 함수 플레이 스칼라 제한 (0) | 2019.11.27 |
---|---|
[SCALA] 왜 스파크는 java.lang.OutOfMemoryError와 함께 실패 않습니다 GC 오버 헤드 제한을 초과? (0) | 2019.11.27 |
[SCALA] 에게 Heroku에 밀어 수 없습니다 (0) | 2019.11.26 |
[SCALA] 어떻게 스칼라의 제품 종류에 대해 생각해야 하는가? (0) | 2019.11.26 |
[SCALA] 스칼라에서 프록시 / 대표 (0) | 2019.11.26 |