[SCALA] 방법에 대한-함축 스칼라와 루프 최적화?
SCALA방법에 대한-함축 스칼라와 루프 최적화?
따라서 스칼라는 빠른 자바로 가정된다. 나는 원래 자바에서 달려 드는 것을 스칼라에서 일부 프로젝트 오일러 문제를 다시 방문하고있다. 특히 문제 5 : "1 내지 20의 숫자를 모두에 의해 나누어 가장 작은 양의 수는 무엇인가"
여기에 내 컴퓨터에 완료하는 데 0.7 초 밖에 걸리지 내 자바 솔루션입니다 :
public class P005_evenly_divisible implements Runnable{
final int t = 20;
public void run() {
int i = 10;
while(!isEvenlyDivisible(i, t)){
i += 2;
}
System.out.println(i);
}
boolean isEvenlyDivisible(int a, int b){
for (int i = 2; i <= b; i++) {
if (a % i != 0)
return false;
}
return true;
}
public static void main(String[] args) {
new P005_evenly_divisible().run();
}
}
여기 1백3초 소요 스칼라에 내 "직접 번역"이다 (이상 147 배!)
object P005_JavaStyle {
val t:Int = 20;
def run {
var i = 10
while(!isEvenlyDivisible(i,t))
i += 2
println(i)
}
def isEvenlyDivisible(a:Int, b:Int):Boolean = {
for (i <- 2 to b)
if (a % i != 0)
return false
return true
}
def main(args : Array[String]) {
run
}
}
마지막으로 여기 39초 소요 함수형 프로그래밍에서 내 시도 (55 시간 이상)의
object P005 extends App{
def isDivis(x:Int) = (1 to 20) forall {x % _ == 0}
def find(n:Int):Int = if (isDivis(n)) n else find (n+2)
println (find (2))
}
윈도우 7 64 비트에 스칼라 2.9.0.1 사용. 어떻게 성능을 향상합니까? 내가 뭔가 잘못하고있는 건가요? 또는 자바는 훨씬 빨리입니까?
해결법
-
==============================
1.이 특정한 경우에 문제는 당신이에 대한 표현 내에서 반환 할 것입니다. 그 차례로 둘러싸는 방법에 적발 된 NonLocalReturnException의 투사로 번역됩니다. 옵티마이 저는 foreach 문을 제거 할 수 있지만 아직 던져 / 캐치를 제거 할 수 있습니다. 그리고 던져 / 캐치 비싸다. 이러한 중첩 반환 스칼라 프로그램에 드문하지만 이후, 옵티마이 저는 아직이 사건을 해결하지 않았다. 희망 곧이 문제를 해결할 최적화를 개선하기 위해 계속 일이있다.
이 특정한 경우에 문제는 당신이에 대한 표현 내에서 반환 할 것입니다. 그 차례로 둘러싸는 방법에 적발 된 NonLocalReturnException의 투사로 번역됩니다. 옵티마이 저는 foreach 문을 제거 할 수 있지만 아직 던져 / 캐치를 제거 할 수 있습니다. 그리고 던져 / 캐치 비싸다. 이러한 중첩 반환 스칼라 프로그램에 드문하지만 이후, 옵티마이 저는 아직이 사건을 해결하지 않았다. 희망 곧이 문제를 해결할 최적화를 개선하기 위해 계속 일이있다.
-
==============================
2.문제는 가능성이 가장 높은 방법 isEvenlyDivisible의 이해를위한 사용하는 것입니다. 이에 상응하는 while 루프에 의해 교체 자바 성능 차이를 제거해야합니다.
문제는 가능성이 가장 높은 방법 isEvenlyDivisible의 이해를위한 사용하는 것입니다. 이에 상응하는 while 루프에 의해 교체 자바 성능 차이를 제거해야합니다.
루프 자바의 반대로 함축 높은 주문 방법에 대한 실제 문법 설탕 있습니다에 대한 스칼라입니다; 이 경우, 당신은 Range 개체에 foreach는 메서드를 호출하고 있습니다. 스칼라는 때때로 고통스러운 성능 리드 매우 일반적이기 때문이지만.
당신은 스칼라 버전 2.9에서 -optimize 플래그를 시도 할 수 있습니다. 관찰 된 성능은 사용중인 특정 JVM에 따라 달라질 수 있습니다, 충분한 가진 옵티마이 JIT는 핫스팟을 식별하고 최적화하는 시간을 "워밍업".
메일 링리스트에 최근의 토론 스칼라 팀이 간단한 경우에 성능 개선에 노력하고 있음을 나타냅니다
여기에 버그 추적기의 문제는 : https://issues.scala-lang.org/browse/SI-4633
업데이트 5/28 :
-
==============================
3.후속으로, 나는 -optimize 플래그를 시도하고 (103) 76 초에서 실행 시간 감소,하지만 자바보다 여전히 107x 느리게 또는 while 루프.
후속으로, 나는 -optimize 플래그를 시도하고 (103) 76 초에서 실행 시간 감소,하지만 자바보다 여전히 107x 느리게 또는 while 루프.
그럼 난 "기능"버전을보고했다 :
object P005 extends App{ def isDivis(x:Int) = (1 to 20) forall {x % _ == 0} def find(n:Int):Int = if (isDivis(n)) n else find (n+2) println (find (2)) }
그리고 간결한 방식에서 "FORALL"제거하는 방법을 알아 내려고 노력. 나는 비참하게 실패와 함께했다
object P005_V2 extends App { def isDivis(x:Int):Boolean = { var i = 1 while(i <= 20) { if (x % i != 0) return false i += 1 } return true } def find(n:Int):Int = if (isDivis(n)) n else find (n+2) println (find (2)) }
이에 내 교활한 5 줄 솔루션 12 선에 balooned있다. 그러나 0.71 초이 버전의 실행, 같은 원래의 자바 버전과 속도, "FORALL"(40.2들)을 사용하여 위의 빠른 버전보다 56 배! (이 빠른 자바보다 이유를 아래 편집 참조)
분명히 내 다음 단계는 자바로 위 등을 번역했지만, 자바는 그것을 처리 할 수와 함께 n은 2만2천마르크 주위에 StackOverflowError가 발생합니다.
그때 나는 조금 내 머리를 긁 라인의 몇 가지를 저장하는 좀 더 꼬리 재귀로, 단지 빨리 실행되지만하자 그것을 직면 "동안"는, 더 읽고 혼란 교체 :
object P005_V3 extends App { def isDivis(x:Int, i:Int):Boolean = if(i > 20) true else if(x % i != 0) false else isDivis(x, i+1) def find(n:Int):Int = if (isDivis(n, 2)) n else find (n+2) println (find (2)) }
스칼라의 꼬리 재귀가 날 승리,하지만 난이 간단 같은 무언가 것을 놀랐어요 "for"루프 (그리고이 방법 "FORALL") 본질적으로 파괴되고있다 그래서 우아에 의해 "그런데", 또는 꼬리 재귀 자세한 교체 할 . 내가 스칼라를 시도하고 이유의 많은 때문에 간결한 구문입니다,하지만 내 코드가 느린 100 번 실행하려고하면 더 좋은 없습니다!
편집 : (삭제)
편집의 편집 : 2.5s 및 0.7s의 실행 시간 사이의 전 불일치는 32 비트 또는 64 비트의 JVM이 사용되었는지에 전적으로 기인했다. 에 관계없이 사용 가능한 경우 자바 64 비트를 사용하는 반면, JAVA_HOME으로 설정되어 어떤 명령 줄 사용에서 스칼라. 십오 일 자신의 설정이 있습니다. 여기에 일부 측정 : Eclipse에서 스칼라 실행 시간
-
==============================
4.이해에 대한 대답은 바로이지만, 전체 이야기 아니에요. 당신은 isEvenlyDivisible에 반환의 사용이 무료 아니라고 메모를주의해야한다. 힘에 대한 스칼라 컴파일러 내부 창을 사용하는 비 로컬 창을 생성하기 위해 (즉 그것의 기능 밖에 돌아).
이해에 대한 대답은 바로이지만, 전체 이야기 아니에요. 당신은 isEvenlyDivisible에 반환의 사용이 무료 아니라고 메모를주의해야한다. 힘에 대한 스칼라 컴파일러 내부 창을 사용하는 비 로컬 창을 생성하기 위해 (즉 그것의 기능 밖에 돌아).
이것은 루프를 종료 예외의 사용을 통해 이루어집니다. 당신은 예를 들어, 자신의 제어 추상화를 구축하는 경우에도 같은 상황이 발생합니다
def loop[T](times: Int, default: T)(body: ()=>T) : T = { var count = 0 var result: T = default while(count < times) { result = body() count += 1 } result } def foo() : Int= { loop(5, 0) { println("Hi") return 5 } } foo()
한 번만 인쇄 "안녕하세요".
참고 foo에의 반환은 (당신이 기대하는 것이 무엇 인) foo는 종료있다. 괄호 표현은 당신이 아닌 지방의 복귀, 출구 foo는 당신의 반환 세력뿐만 아니라 몸을 생성하기 위해이 힘을 루프의 서명에 컴파일러를 볼 수있는 기능 문자이기 때문에.
자바에서 (즉 JVM)과 같은 동작을 구현하는 유일한 방법은 예외가 발생하는 것입니다.
isEvenlyDivisible에 다시가는 :
def isEvenlyDivisible(a:Int, b:Int):Boolean = { for (i <- 2 to b) if (a % i != 0) return false return true }
는 IF (a % 나! = 0)에 복귀 거짓 복귀 치면마다 있도록 런타임 던져 헤드 GC 꽤 발생 예외를 캐치하는, 리턴을 갖는 함수 리터럴.
-
==============================
5.내가 발견 한 FORALL 방법을 빠르게하는 몇 가지 방법 :
내가 발견 한 FORALL 방법을 빠르게하는 몇 가지 방법 :
원래 : 41.3의
def isDivis(x:Int) = (1 to 20) forall {x % _ == 0}
그래서 우리는 새로운 범위를 매번 생성하지 않는, 범위를 사전은-인스턴스 : 9.0들
val r = (1 to 20) def isDivis(x:Int) = r forall {x % _ == 0}
대신 범위의 목록으로 변환 : 4.8들
val rl = (1 to 20).toList def isDivis(x:Int) = rl forall {x % _ == 0}
나는 몇 가지 다른 컬렉션을 시도했지만 목록은 빠른 (우리가 범위 및 고차 기능 모두를 피할 것보다 7 배 여전히 비록 느린)이었다.
나는 스칼라에 새로운 오전 동안, 나는 컴파일러가 쉽게 간단하게 자동으로 바깥 쪽 범위에 범위 상수 (위와 같이) 방법의 범위 리터럴을 대체하여 신속하고 상당한 성능 향상을 구현할 수 추측에는 요. 또는 더 나은, 자바에서 문자열 리터럴과 같은 인턴을.
각주: 배열 범위와 거의 동일했지만, 흥미롭게도, (아래) 새로운 FORALL 방법 포주하면 24 % 64 비트에 빠른 실행 및 빠른 32 비트에서 8 %의 결과. 나는 15에 20에서 사라의 차이를 요인의 수를 줄임으로써 계산 크기를 줄일 때, 어쩌면 그것은 가비지 콜렉션 효과입니다. 연장 기간 동안 최대 부하에서 작동 할 때에는 원인이 무엇이든간에, 그것은 중요합니다.
목록에 대한 유사한 포주는 약 10 % 더 나은 성능 결과.
val ra = (1 to 20).toArray def isDivis(x:Int) = ra forall2 {x % _ == 0} case class PimpedSeq[A](s: IndexedSeq[A]) { def forall2 (p: A => Boolean): Boolean = { var i = 0 while (i < s.length) { if (!p(s(i))) return false i += 1 } true } } implicit def arrayToPimpedSeq[A](in: Array[A]): PimpedSeq[A] = PimpedSeq(in)
-
==============================
6.난 그냥 이러한 종류의 문제는 모든 기능 언어에 대한 단지의 성능에 올 것을이 같은 문제를 통해 스칼라에 대한 믿음을 잃을 수도 있습니다 사람들을 위해 코멘트를 원했다. 당신이 하스켈에 접어 최적화하는 경우, 당신은 종종 재귀 꼬리 통화에 최적화 된 루프로 쓰기 다시해야한다, 그렇지 않으면 당신은에 맞설 수있는 성능 및 메모리 문제가 있습니다.
난 그냥 이러한 종류의 문제는 모든 기능 언어에 대한 단지의 성능에 올 것을이 같은 문제를 통해 스칼라에 대한 믿음을 잃을 수도 있습니다 사람들을 위해 코멘트를 원했다. 당신이 하스켈에 접어 최적화하는 경우, 당신은 종종 재귀 꼬리 통화에 최적화 된 루프로 쓰기 다시해야한다, 그렇지 않으면 당신은에 맞설 수있는 성능 및 메모리 문제가 있습니다.
나는 FPS 아직 우리는 이런 것들에 대해 생각하지 않는 점에 최적화되지 않은 것은 불행한 알고, 그러나 이것은 전혀 스칼라 특히 문제가되지 않습니다.
-
==============================
7.스칼라 문제의 특정 이미 논의되었다, 그러나 가장 큰 문제는 무차별 알고리즘을 사용하는 것은 매우 냉각되지 않는 것입니다. (훨씬 더 빨리 원래의 자바 코드보다)이 고려 :
스칼라 문제의 특정 이미 논의되었다, 그러나 가장 큰 문제는 무차별 알고리즘을 사용하는 것은 매우 냉각되지 않는 것입니다. (훨씬 더 빨리 원래의 자바 코드보다)이 고려 :
def gcd(a: Int, b: Int): Int = { if (a == 0) b else gcd(b % a, a) } print (1 to 20 reduce ((a, b) => { a / gcd(a, b) * b }))
-
==============================
8.프로젝트 오일러의 솔루션 스칼라에 주어진 한 줄을보십시오
프로젝트 오일러의 솔루션 스칼라에 주어진 한 줄을보십시오
주어진 시간은 While 루프에서 비록 멀리, 더 빨리 당신보다 적어도입니다 .. :)
from https://stackoverflow.com/questions/6146182/how-to-optimize-for-comprehensions-and-loops-in-scala by cc-by-sa and MIT license
'SCALA' 카테고리의 다른 글
[SCALA] => 수행하고 무엇 () => 스칼라의 평균 [중복] (0) | 2019.11.10 |
---|---|
[SCALA] 간단한 경우 클래스에 대한 주문을 정의하기 쉬운 관용적 방법 (0) | 2019.11.10 |
[SCALA] 무엇 스칼라 연속 요청은 왜 그들을 사용? (0) | 2019.11.10 |
[SCALA] HLists는 튜플을 작성하는 뒤얽힌 방식보다 더 아무것도? (0) | 2019.11.10 |
[SCALA] 스파크 작업을 작성할 때 SparkContext.getOrCreate를 사용하지 않는 이유가 있나요? (0) | 2019.11.10 |