2013-09-23 2 views
4

스칼라와 Java 성능간에 매우 이상한 차이가 발생했습니다. Java에서 inversion counting 루틴을 구현 한 다음 모든 스칼라 스칼라 버전 (List 또는 Stream 사용)이 스택 오버플로/메모리 부족 오류로 인해 매우 느리거나 충돌하기 때문에 Scala로 줄 단위로 이식했습니다. 하지만이 버전은 느린 편 이었지만 Java 버전은 100,000 개의 정수 배열을 처리하는 데 22ms가 걸렸지 만 Scala 버전은 3 초가 걸렸습니다. 스칼라 버전의 관련 코드는 다음과 같습니다.스칼라 대 Java 성능

def mergeAndCountInversions(xs: Array[Int], aux: Array[Int], left: Int, right: Int) = { 
    xs.copyToArray(aux) 
    val m = left + (right - left)/2 

    var i = left 
    var j = m + 1 
    var inv: Long = 0 

    for (k <- left to right) { 
     if (i > m) { 
     xs(k) = aux(j) 
     j += 1 
     } else if (j > right) { 
     xs(k) = aux(i) 
     i += 1 
     } else if (aux(j) < aux(i)) { 
     xs(k) = aux(j) 
     j += 1 
     inv += (m - i) + 1 
     } else { 
     xs(k) = aux(i) 
     i += 1 
     } 
    } 
    inv 
    } 

이 루틴의 성능을 향상시키는 방법에 대한 아이디어가 있습니까?

UPD : Scala 버전의 성능 저하는 완전히 내 잘못입니다. 첫 번째 문은 전체 배열을 보조 배열에 불필요하게 복사합니다. 필요한 부분 만 복사하도록 변경하면 Java와 성능이 비슷합니다.

+0

이 질문에 대한 답변을 작성하기에는 스칼라가 충분하지 않지만 스칼라의 배열 인덱싱은 배열 객체에 대해 'apply' 메소드를 호출하는 것으로 간주합니다. Java에서 수행하는 포인터 연산과 똑같은 것은 아닙니다 후드는 대괄호로 배열 액세스 구문을 사용할 때! – kqr

+5

우선 : [jmh] (http://openjdk.java.net/projects/code-tools/jmh/)를 사용하여 예열 후 성능 **을 측정하십시오. 인라인 한 후에도 동일한 성능을 얻을 것입니다. 또한 (Scalaxy) (https://github.com/ochafik/Scalaxy/tree/master/Loops)를 사용하여 컴파일 타임에 'for (k <- 왼쪽에서 오른쪽으로 최적화)'와 같이 인라이닝을 적용 할 수 있습니다. – senia

+0

@senia 벤치 마크 루틴을 사용하여 질문을 업데이트했습니다. – synapse

답변

1

대부분 이해하기 쉽기 때문일 수 있습니다.

Range(left, right).foreach { k => 
    // code... 
} 

Java 솔루션과 비교할 수 있도록하려면 while 루프로 교체해야합니다.

var k = left 

while (k <= right) { 
    // code... 

    k += 1 
} 

나는이 솔루션이 Java 버전과 동등하다고 확신한다.

+0

그래, 그게 이유가 될 수 있지만 성능이'while '과 동일하다고 생각 했어 – synapse

+0

왜 내가 이해하지 못하는 것은 스칼라 컴파일러가'Range (...)를 최적화하는 방법을 찾지 못한 이유이다. foreach' Java for 루프를 위해 생성되는 것과 동일한 기본 바이트 코드로 구성하십시오. –

+0

@ErikAllik for scala는 일반적인 Java 루핑 구문보다 훨씬 많은 작업을 수행합니다. 가장 단순한 경우를 최적화하기 위해 (https://issues.scala-lang.org/browse/SI-1338) 작업을 수행하고 있습니다. –