스칼라 대 자바, 성능 및 메모리? [닫은]


160

스칼라를 살펴보고 싶어하며 답을 찾을 수없는 기본적인 질문이 하나 있습니다. 일반적으로 스칼라와 Java의 메모리 성능 및 사용에 차이가 있습니까?


3
성능이 매우 비슷하다고 주장하는 것을 들었습니다. 나는 그것이 당신이하는 일에 크게 의존한다고 생각합니다. (Java vs C와 마찬가지로)
Peter Lawrey

이러한 종류의 질문에 대한 답은 "의존적"입니다-시스템 X와 시스템 Y의 사실상 비교를 위해. 이것은 또한 stackoverflow.com/questions/2479819/…
James Moore

답변:


261

스칼라는 엄청난 양의 메모리를 사용하지 않고도 사용하기가 매우 쉽습니다. 이것은 일반적으로 매우 강력하지만 때로는 성 가실 수 있습니다. 예를 들어, 문자열 배열 ( array)과 해당 문자열을 파일 ( mapping) 로 매핑 한다고 가정합니다 . 맵에 있고 길이가 2보다 큰 문자열에서 온 모든 파일을 가져오고 싶다고 가정하십시오. 자바에서는

int n = 0;
for (String s: array) {
  if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
  if (s.length <= 2) continue;
  bigEnough[n++] = map.get(s);
}

아휴! 힘든 일. 스칼라에서 동일한 작업을 수행하는 가장 간단한 방법은 다음과 같습니다.

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)

쉬운! 그러나 컬렉션이 작동하는 방식에 대해 잘 알고 있지 않는 한이 방법을 사용하면 추가 중간 배열 (with filter)과 배열의 모든 요소 (with mapping.get, 옵션). 또한 함수 객체가 작기 때문에 두 가지 함수 객체 (필터 용 필터와 flatMap 용 필터)를 만듭니다.

따라서 기본적으로 메모리 사용량은 기본 수준에서 동일합니다. 그러나 Scala의 라이브러리에는 많은 수의 (보통 수명이 짧은) 객체를 매우 쉽게 만들 수있는 많은 강력한 방법이 있습니다. 가비지 수집기는 일반적으로 이러한 종류의 가비지에 매우 적합하지만 사용중인 메모리에 대해 완전히 알지 못하는 경우 Java보다 Scala에서 더 빨리 문제가 발생할 수 있습니다.

Computer Languages ​​Benchmark Game Scala 코드는 Java와 유사한 성능을 얻기 위해 다소 Java와 유사한 스타일로 작성되므로 Java와 같은 메모리 사용이 있습니다. Scala에서이를 수행 할 수 있습니다. 고성능 Java 코드처럼 보이도록 코드를 작성하면 고성능 Scala 코드가됩니다. (당신은 할 수 있습니다 더 관용적 스칼라 스타일을 작성하고 여전히 좋은 성능을 얻을 수 있지만, 세부 사항에 따라 달라집니다.)

프로그래밍 시간에 따라 스칼라 코드는 일반적으로 Java 코드보다 빠릅니다 . 스칼라에서는 번거롭지 않은 성능 비판적 부분을 적은 노력으로 수행 할 수 있으며 알고리즘 최적화 및 집중에 많은 관심을 기울일 수 있기 때문에 성능에 중요한 부분을위한 코드.


172
마지막 단락에 +1 그것은 고려의 밖으로 남아있는 중요한 포인트입니다 멀리 너무 자주.
케빈 라이트

2
나는 당신이 언급 한 문제에 견해 가 많은 도움이 될 것이라고 생각했습니다 . 아니면 배열, 특히 그렇지 않습니까?
Nicolas Payette

1
@ 케빈 라이트 (Kevin Wright)- "이것은 너무 자주 고려하지 않아도되는 중요한 포인트입니다."-말하기 쉽고 설명하기 어려운 것입니다. 다른 사람이 덜 숙련 된 것이 아닌 Rex Kerr의 기술에 대해 알려주세요.
igouy

1
>> 최고의 성능을 갖춘 관용적 스타일로 << 벤치마킹 게임에는 "최상의"성능을 달성하지 못하는 관용적 스칼라 프로그램을위한 공간이 있습니다.
igouy

2
@RexKerr-Java 예제는 문자열을 선택한 후 스칼라 예제가 한 번만 수행하는 가능한 각 문자열에 대해 매핑 키를 두 번 조회하지 않습니까? 즉, 서로 다른 데이터 세트에 대해 다른 방식으로 최적화되어 있습니까?
세스

103

저는 새로운 사용자이므로 위의 Rex Kerr의 답변에 의견을 추가 할 수 없습니다 (새로운 사용자는 "응답"하지만 "댓글"은 허용하지 않는 것이 매우 이상한 규칙입니다).

나는 위의 Rex의 대중적인 대답에 대한 "조류, 자바는 너무 장황하고 열심히 일한다"라는 응답에 간단히 서명했다. 물론 더 간결한 스칼라 코드를 작성할 수는 있지만 주어진 Java 예제는 분명히 부풀어 오른다. 대부분의 Java 개발자는 다음과 같은 코드를 작성합니다.

List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
  if(s.length() > 2 && mapping.get(s) != null) {
    bigEnough.add(mapping.get(s));
  }
}

물론 Eclipse가 실제 입력을 대부분 수행하지 않고 저장된 모든 문자가 실제로 더 나은 프로그래머가되는 것처럼 가장한다면 다음과 같이 코딩 할 수 있습니다.

List b=new ArrayList();
for(String s:array)
  if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));

이제는 전체 변수 이름과 중괄호를 입력하는 데 걸리는 시간을 절약 할뿐만 아니라 (깊숙한 알고리즘 적 사고를 생각하는 데 5 초 더 걸릴 수 있음) 난독 화 대회에 코드를 입력하고 잠재적으로 추가 현금을 얻을 수 있습니다 공휴일.


7
어떻게 "이 달의 언어"클럽 회원이 아니십니까? 좋은 의견. 나는 특히 마지막 단락을 읽는 것을 즐겼습니다.
stepanian

21
잘 넣어! 팽창 된 Java 코드 다음에 신중하게 구성된 스칼라 (또는 다른 FP 언어)의 몇 가지 예가 이어지고 스칼라가 Java보다 우수해야한다는 급한 결론이 나옵니다. 어쨌든 스칼라에서 중요한 것을 쓴 사람은 누구입니까? ;-) 그리고 트위터를 말하지 마세요 ...
chrisjleu

2
Rex의 솔루션은 어레이의 메모리를 미리 할당하여 컴파일 된 코드 를 더 빠르게 실행합니다 (접근 방식으로 인해 JVM이 주기적으로 어레이를 재배치하도록 할 수 있습니다). 더 많은 타이핑이 필요했지만 성능 측면에서는 승자가 될 수 있습니다.
Ashalynd

5
우리가 그것에있는 동안, java8에서는 :Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
bennyl

2
Scala가 Java보다 어떤 방식으로 "더 나은"이유는보다 일반적인 패턴을 Monads, Functors 등과 같은 유형으로보다 쉽게 ​​표현할 수있는 확장 된 유형 시스템 기능입니다. 이를 통해 Java에서 자주 발생하는 것처럼 지나치게 엄격한 계약으로 인해 방해받지 않는 유형을 만들 수 있습니다. 코드의 실제 패턴을 기반으로하지 않는 엄격한 계약은 코드를 올바르게 단위 테스트하기 위해 책임 패턴의 반전이 필요한 이유입니다 (Dependence Injection이 먼저 필요하고 XML 지옥이 중요합니다). addl. 유연성이 제공하는 간결함은 단지 보너스입니다.
josiah

67

스칼라를 Java와 같이 작성하면 거의 동일한 메트릭으로 거의 동일한 바이트 코드가 생성 될 것으로 예상 할 수 있습니다.

불변 개체와 고차 함수를 사용하여 더 "관용적으로"작성하면 조금 느리고 커집니다. 이 규칙에 대한 한 가지 예외는 type params가 사용하는 일반 객체를 사용할 때입니다.@specialised 주석을 권투 / 언 박싱을 피함으로써 Java 성능을 능가하는 더 큰 바이트 코드를 생성한다는 것입니다.

또한 병렬로 실행할 수있는 코드를 작성할 때 더 많은 메모리와 더 적은 속도가 불가피한 트레이드 오프라는 사실도 언급 할 가치가 있습니다. 관용 스칼라 코드는 일반적인 Java 코드보다 본질적으로 훨씬 선언적이며 종종 4 자 (.par 완전히 평행을 이루는 데 ) 정도 떨어져 있습니다.

그래서 만약

  • 스칼라 코드는 단일 스레드에서 Java 코드보다 1.25 배 더 오래 걸립니다
  • 4 개의 코어 로 쉽게 분할 할 수 있습니다 (현재 랩톱에서도 일반적 임)
  • 병렬 실행 시간 (1.24 / 4 =) : 원래 Java의 0.3125 배

스칼라 코드가 25 % 느리거나 3 배 빠르다고 말할 수 있습니까?

정답은 "성능"을 정의하는 방법에 따라 다릅니다.


4
덧붙여서, 당신은 .par2.9 에 언급하고 싶을 것 입니다.
렉스 커

26
>> 스칼라 코드가 25 % 더 느리거나 3 배 더 빠르다고 말할 수 있습니까? << 왜 다중 스레드 Java 코드에 대한 가상 비교가 아닌가?
igouy

17
@igouy-요점은 가상 코드가 존재하지 않는다는 것입니다. "빠른"Java 코드의 필수 특성으로 인해 병렬화하기가 훨씬 더 어려워 비용 / 혜택 비율이 전혀 발생하지 않을 것입니다. 반면에 관용적 스칼라는 본질적으로 훨씬 더 선언적 일뿐 아니라 사소한 변화만으로도 종종 이루어질 수있다.
케빈 라이트

7
동시 Java 프로그램이 있다고해서 일반적인 Java 프로그램을 동시성에 쉽게 적용 할 수 있음을 의미하지는 않습니다 . 어쨌든 특정 포크 조인 스타일은 Java에서 특히 드물고 명시 적으로 코딩해야하지만, 최소 포함 값 찾기 또는 컬렉션의 값 합계와 같은 간단한 작업은 사소하게 병렬로 수행 할 수 있다고 말합니다. 스칼라에서 단순히를 사용하여 .par.
케빈 라이트

5
아뇨. 이런 종류의 것은 많은 알고리즘의 기본 구성 요소이며, 언어 및 표준 라이브러리 (일반 프로그램뿐만 아니라 모든 프로그램에서 사용하는 동일한 표준 라이브러리)에서 낮은 수준으로 존재하는 것을 보는 것은 단순히 언어를 선택함으로써 동시성에 더 가깝습니다. 예를 들어 컬렉션에 대한 매핑은 본질적으로 병렬화에 적합 map하며이 방법을 사용하지 않는 Scala 프로그램의 수는 거의 줄어 듭니다.
케빈 라이트

31

컴퓨터 언어 벤치 마크 게임 :

속도 테스트 java / scala 1.71 / 2.25

메모리 테스트 java / scala 66.55 / 80.81

따라서이 벤치 마크에서는 Java가 24 % 더 빠르며 스칼라는 21 % 더 많은 메모리를 사용한다고 말합니다.

전체적으로 큰 문제는 아니며 대부분의 시간이 데이터베이스와 네트워크에 의해 소비되는 실제 앱에서는 중요하지 않습니다.

결론 : 스칼라가 당신과 당신의 팀 (그리고 당신이 떠날 때 프로젝트를 인수하는 사람들)을 더 생산적으로 만들면, 그것을 사용해야합니다.


34
코드 크기 java / scala 3.39 / 2.21
hammar

22
이와 같은 숫자에주의하십시오. 실제로 그들은 거의 의미가 없지만 끔찍하게 정확합니다. 스칼라가 평균적으로 자바보다 항상 24 % 더 빠르지는 않습니다.
Jesper

3
Afaik에서 인용 한 숫자는 그 반대입니다. Java는 스칼라보다 24 % 빠릅니다. 그러나 당신이 말했듯이, 그들은 마이크로 벤치 마크이며 실제 앱에서 일어나는 일과 일치하지 않아도됩니다. 그리고 다른 언어로 된 다른 방법이나 문제 해결책은 결국 비교 프로그램이 덜 나올 수 있습니다.
알 수없는 사용자

9
"스칼라가 당신과 당신의 팀을 만든다면 ..."결론 : 당신은 전에 :-) 후에 알게 될 것입니다
igouy

벤치 마크 게임 도움말 페이지는 "2 가지 언어 구현을위한 프로그램 속도 및 크기 비교"방법의 예를 제공합니다. 스칼라와 자바의 경우 적절한 비교 웹 페이지는 다음과 같습니다. shootout.alioth.debian.org/u64q/scala.php
igouy

20

내가 언급 한 Rex Kerr의 예제간에 명백한 성능 차이가있는 것처럼 보이지만 다른 사람들은 타이트 루프와 관련 하여이 질문에 대답했습니다.

이 답변은 실제로 설계 결함으로 타이트한 루프 최적화의 필요성을 조사 할 수있는 사람들을 대상으로합니다.

나는 스칼라를 처음 접했지만 (약 1 년 정도), 지금까지 느낌은 당신이 연기 할 수 있다는 것입니다 은 디자인, 구현 및 실행의 여러 측면을 비교적 쉽게 (충분한 배경 ​​읽기 및 실험

지연된 디자인 기능 :

지연된 구현 기능 :

지연된 실행 기능 : (죄송합니다, 링크가 없습니다)

  • 스레드 안전 게으른 값
  • 이름 별
  • 수도원 물건

이 기능은 빠르고 엄격한 응용 프로그램으로가는 길을 걷는 데 도움이되는 기능입니다.


Rex Kerr의 예제는 실행 측면이 지연되는 점이 다릅니다. Java 예제에서 메모리 할당은 스칼라 예제가 맵핑 조회를 지연시키는 크기가 계산 될 때까지 지연됩니다. 나에게는 완전히 다른 알고리즘처럼 보입니다.

다음은 Java 예제에 해당하는 사과 대 사과에 가깝습니다.

val bigEnough = array.collect({
    case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})

어떤 중간 컬렉션도없고, Option인스턴스 등이 또한 이렇게 수집 유형 보존 bigEnough의 유형이다 Array[File]- Arraycollect 구현 아마 씨 커의 자바 코드가 수행하는 작업의 라인을 따라 뭔가를하고있는 것입니다.

위에 나열된 지연된 디자인 기능을 통해 Scala의 수집 API 개발자는 API를 중단하지 않고 향후 릴리스에서 빠른 어레이 별 수집 구현을 구현할 수 있습니다. 이것이 속도로가는 길을 언급하는 것입니다.

또한:

val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)

withFilter내가 대신 여기에 사용했다고 방법을 filter수정 중간 수집 문제를하지만 옵션 인스턴스 문제가 여전히 존재한다.


스칼라에서 간단한 실행 속도의 한 예는 로깅입니다.

Java에서는 다음과 같이 작성할 수 있습니다.

if (logger.isDebugEnabled())
    logger.debug("trace");

스칼라에서 이것은 다음과 같습니다.

logger.debug("trace")

스칼라에서 디버깅 할 메시지 매개 변수는 " => String" 타입을 가지고 있기 때문에 평가할 때 실행되는 매개 변수가없는 함수로 생각되지만 문서는 이름으로 전달합니다.

편집 {스칼라의 함수는 객체이므로 여기에 추가 객체가 있습니다. 내 작품에서 사소한 객체의 무게는 로그 메시지가 불필요하게 평가 될 가능성을 제거 할 가치가 있습니다. }

이렇게하면 코드가 더 빨라지지는 않지만 더 빨라질 수 있으며 다른 사람의 코드를 대량으로 정리하고 정리하는 경험이 줄어 듭니다.

나에게 이것은 스칼라에서 일관된 테마입니다.


하드 코드는 Scala가 조금 더 힌트가 있지만 왜 더 빠른지 캡처하지 못합니다.

스칼라에서는 코드 재사용과 코드 품질 한도의 조합이라고 생각합니다.

Java에서 멋진 코드는 종종 이해할 수없는 혼란이되므로 대부분의 프로그래머가 사용할 수 없으므로 프로덕션 품질 API 내에서 실제로 실행되지 않습니다.

스칼라가 아인슈타인이 DSL을 통해 표현 될 수있는 훨씬 더 유능한 API를 구현할 수 있기를 희망합니다. 스칼라의 핵심 API는 이미이 경로를 따르고 있습니다.


로깅 항목은 Scala의 성능 함정에 대한 좋은 예입니다. logger.debug ( "trace")는 매개 변수없는 함수에 대한 새 개체를 만듭니다.
jcsahnwaldt Reinstate Monica

실제로-그것이 내 관련 지점에 어떤 영향을 미칩니 까?
세스

전술 한 목적은 또한 효율성을 위해 투명한 IoC 제어 구조를 만드는 데 사용될 수있다. 예, Java에서 동일한 결과가 이론적으로 가능하지만 코드 작성 방식에 크게 영향을 미치거나 혼란 스러울 수 있습니다. 따라서 소프트웨어 개발의 많은 요소를 연기하는 Scala의 요령이 더 빠른 코드로 나아가는 데 도움이된다는 주장은 더 큽니다. 실제로 더 빠른 단위 성능을 갖는 것보다 더 빠릅니다.
세스

좋아, 나는 이것을 다시 읽고 나는 "간단한 실행 속도"를 썼다 – 나는 메모를 추가 할 것이다. 좋은 지적 :)
세스

3
예측 가능한 if 문 (기본적으로 슈퍼 스칼라 프로세서에서 사용 가능) 대 객체 할당 + 가비지. Java 코드는 분명히 더 빠릅니다 (조건 만 평가하면 실행이 로그 문에 도달하지 않습니다). "
Eloff


10

Java와 Scala는 모두 JVM 바이트 코드로 컴파일되므로 차이점은 그다지 크지 않습니다. 가장 좋은 비교는 아마도 컴퓨터 언어 벤치 마크 게임 일 것입니다. 기본적으로 Java와 Scala는 모두 동일한 메모리 사용량을 가지고 있습니다. 스칼라는 열거 된 일부 벤치 마크에서 Java보다 약간 느리지 만 프로그램의 구현이 다르기 때문일 수 있습니다.

하지만 둘 다 너무 가까워 걱정할 필요가 없습니다. 스칼라와 같이보다 표현력이 뛰어난 언어를 사용함으로써 얻을 수있는 생산성 향상은 최소한의 성능 저하보다 훨씬 가치가 있습니다.


7
논리적 인 오류가 있습니다. 두 언어가 바이트 코드로 컴파일되지만 숙련 된 프로그래머와 초보자는 코드가 바이트 코드로 컴파일되지만 동일한 바이트 코드로 컴파일되지 않으므로 결론은 차이가 그렇게 클 수 없습니다 잘못되었을 수 있습니다. 사실, 과거에는 while 루프가 의미 적으로 동등한 for-loop보다 스칼라에서 훨씬 더 빠를 수 있습니다 (정확하게 기억한다면 오늘날 훨씬 낫습니다). 물론 둘 다 바이트 코드로 컴파일되었습니다.
사용자가 알 수 없음

@user unknown- "어떻게 while-loop는 의미 상 동등한 for-loop보다 scala에서 훨씬 더 빠를 수 있습니다"-Scala 벤치 마크 게임 프로그램은 while 루프로 작성됩니다.
igouy

@igouy : 나는이 마이크로 벤치 마크의 결과에 대해 이야기하지 않았지만 논쟁에 대해 이야기하지 않았습니다. 내가 보여주고 자하는 문제의 진술 Java and Scala both compile down to JVM bytecode, 과 결합 된 진정한 진술 은 논증적인 결론이 아니라 수사법 이라는 것을 보여줍니다 . sodiffence isn't that big.so
사용자가 알 수 없음

3
엄청나게 높은 투표로 놀랍게도 정답.
shabunc

4

Java 예제는 실제로 일반적인 응용 프로그램의 관용구가 아닙니다. 이러한 최적화 된 코드는 시스템 라이브러리 메소드에서 찾을 수 있습니다. 그러나 올바른 유형의 배열, 즉 File []을 사용하고 IndexOutOfBoundsException을 발생시키지 않습니다. (카운팅 및 추가를위한 다른 필터 조건). 내 버전은 (중괄호로 항상 (!)입니다. Eclipse에서 단일 키를 누르기 위해 2 초를 절약하여 소개 된 버그를 검색하는 데 시간을 소비하지 않기 때문에) :

List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
  if(s.length() > 2) {
    File file = mapping.get(s);
    if (file != null) {
      bigEnough.add(file);
    }
  }
}

그러나 현재 프로젝트에서 다른 못생긴 Java 코드 예제를 많이 가져올 수 있습니다. 일반적인 구조와 동작을 고려하여 일반적인 복사 및 수정 스타일의 코딩을 피하려고했습니다.

내 추상 DAO 기본 클래스에는 공통 캐싱 메커니즘에 대한 추상 내부 클래스가 있습니다. 모든 구체적인 모델 오브젝트 유형에 대해 추상 DAO 기본 클래스의 서브 클래스가 있으며, 내부 클래스는 비즈니스 오브젝트가 데이터베이스에서로드 될 때 비즈니스 오브젝트를 작성하는 메소드에 대한 구현을 제공하기 위해 서브 클래 싱됩니다. 독점 API를 통해 다른 시스템에 액세스하므로 ORM 도구를 사용할 수 없습니다.

이 서브 클래 싱 및 인스턴스화 코드는 Java에서 전혀 명확하지 않으며 Scala에서 매우 읽기 쉽습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.