현대 OO 언어가 C ++의 어레이 저장소 성능과 경쟁 할 수 있습니까?


40

방금 내가 친숙한 모든 현대 OO 프로그래밍 언어 (기본적으로 Java, C # 및 D)가 공변량 배열을 허용한다는 것을 알았습니다. 즉, 문자열 배열은 객체 배열입니다.

Object[] arr = new String[2];   // Java, C# and D allow this

공변량 배열은 정적 유형 시스템의 구멍입니다. 컴파일 타임에 감지 할 수없는 유형 오류가 가능하므로 런타임에 배열에 대한 모든 쓰기를 확인해야합니다.

arr[0] = "hello";        // ok
arr[1] = new Object();   // ArrayStoreException

많은 어레이 저장소를 사용하면 성능이 크게 저하되는 것처럼 보입니다.

C ++에는 공 변형 배열이 없으므로 런타임 검사를 수행 할 필요가 없으므로 성능 저하가 없습니다.

필요한 런타임 점검 횟수를 줄이기 위해 분석이 수행 되었습니까? 예를 들어 내가 말하면 :

arr[1] = arr[0];

가게는 실패 할 수 없다고 주장 할 수 있습니다. 내가 생각하지 못한 다른 가능한 최적화가 많이 있다고 확신합니다.

현대의 컴파일러는 실제로 이러한 종류의 최적화를 수행합니까, 아니면 예를 들어 Quicksort는 항상 O (n log n) 불필요한 런타임 검사를 수행한다는 사실에 따라 살아야합니까?

현대 OO 언어는 공 변형 배열을 지원하여 생성되는 오버 헤드를 피할 수 있습니까?


7
C ++이 지원하지 않는 기능에서 C ++이 다른 언어보다 빠르다고 제안하는 이유가 혼란 스럽습니다.

17
@eco : C ++은 공 변형 배열을 지원하지 않기 때문에 배열 액세스에서 더 빠릅니다 . Fred는 "현대 OO 언어"가 공변량 배열의 오버 헤드를 제거하여 C ++ 속도에 가까워 질 수 있는지 알고 싶어합니다.
Mooing Duck

13
"런타임에서 컴파일하지만 예외를 던지는 코드는 나쁜 소식입니다"

4
@Jesse 수백만의 사람들이 동적 언어로 안정적이고 확장 성이 뛰어난 코드를 작성합니다. Imo : 코드에 대한 테스트 사례를 작성하지 않으면 컴파일러 오류가 있는지 여부는 상관하지 않습니다. 어쨌든 그것을 신뢰하지는 않습니다.
Voo

7
@Jesse 그리고 언제 예외를 기대하지만 런타임에? 문제는 런타임에 예외를 던지는 코드가 아닙니다. 좋은 의미가있는 많은 경우가 있습니다. 문제는 컴파일러에 의해 정적으로 잡히지 않고 대신 예외가 발생하는 잘못된 것으로 보장 되는 코드입니다 실행 시간.
Jonathan M Davis

답변:


33

D 에는 공변량 배열 이 없습니다 . 최신 릴리스 ( dmd 2.057 ) 이전에 허용 되었지만 해당 버그 가 수정되었습니다.

D의 배열은 사실상 포인터와 길이를 가진 구조체입니다.

struct A(T)
{
    T* ptr;
    size_t length;
}

바운드 검사는 일반적으로 배열을 인덱싱 할 때 수행되지만으로 컴파일하면 제거됩니다 -release. 따라서 릴리스 모드에서는 C / C ++의 배열과 D의 배열간에 실제 성능 차이가 없습니다.


표시되지 않음 -d-programming-language.org/arrays.html 은 "정적 배열 T[dim]은 다음 중 하나로 암시 적으로 변환 될 수 있습니다. ... U[]... 동적 배열 T[]은 다음 중 하나로 암시 적으로 변환 될 수 있습니다 U[].. .. 어디 U기본 클래스입니다 T. "
Ben Voigt

2
@BenVoigt 그런 다음 온라인 문서를 업데이트해야합니다. 불행히도 항상 100 % 최신 상태는 아닙니다. 변환은 함께 작동합니다 const U[]다음 배열의 요소에 잘못된 유형을 지정할 수 없기 때문에,하지만 T[]않습니다 확실히 하지 로 변환 U[]만큼으로 U[]변경할 수 있습니다. 이전과 마찬가지로 공변량 배열을 허용하면 심각한 설계 결함이 수정되었습니다.
Jonathan M Davis

@JonathanMDavis : 공변량 배열은 불안정하지만 동일한 배열에서 읽은 배열 요소 에만 쓰이는 특정 코드 시나리오에 적합합니다 . D는 어떻게 임의의 유형의 배열을 정렬 할 수있는 메소드를 작성할 수 있습니까?
supercat

@supercat 임의의 유형을 정렬하는 함수를 작성하려면 템플릿 화하십시오. 예 : dlang.org/phobos/std_algorithm.html#sort
Jonathan M Davis

21

예, 한 가지 중요한 최적화는 다음과 같습니다.

sealed class Foo
{
}

C #에서이 클래스는 모든 유형의 수퍼 타입 ​​일 수 없으므로 type 배열의 검사를 피할 수 있습니다 Foo.

그리고 두 번째 질문으로, F # 공 변형 배열은 허용되지 않습니다 (그러나 런타임에 최적화에서 불필요한 것으로 확인되지 않는 한 체크는 CLR에 남아있을 것입니다)

let a = [| "st" |]
let b : System.Object[] = a // Fails

https://stackoverflow.com/questions/7339013/array-covariance-in-f

다소 관련된 문제는 배열 바운드 검사입니다. 이것은 CLR에서 수행 된 최적화에 대한 흥미로운 (그러나 오래된) 읽을 수 있습니다 (공분산은 1 위를 언급합니다) : http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds -clr.aspx에서 검사 제거


2
스칼라는 또한이 구성을 막는다 : val a = Array("st"); val b: Array[Any] = a불법이다. (그러나 기본 JVM이 사용되기 때문에 스칼라의 배열은 ... 특별한 마법입니다.)
pst

13

자바 답변 :

실제로 코드를 벤치마킹하지 않은 것으로 생각합니까? 일반적으로 Java의 모든 동적 캐스트 중 90 %는 무료입니다 .JIT가 JIT를 생략 할 수 있기 때문에 (quicksort는 이에 대한 좋은 예입니다) 나머지 ld/cmp/br는 절대적으로 예측 가능한 하나의 시퀀스입니다 (그렇지 않은 경우, 왜 코드가 던지는 지 잘 알 수 있습니다) 모든 동적 캐스트 예외?).

우리는 실제 비교보다 훨씬 빨리로드를 수행합니다. 분기는 모든 경우의 99.9999 % (통계 구성!)로 올바르게 예측되므로 파이프 라인을 멈추지 않습니다 (로드로 인해 메모리에 충돌하지 않는 경우) 잘 눈에 띄지 않지만 하중은 어쨌든 필요합니다). 따라서 JIT가 점검을 전혀 피할 수없는 경우 비용은 1 클럭 사이클입니다.

약간의 오버 헤드? 물론입니다.


내 답변을 지원하기 위해 Java 대 C 성능에 대해 논의하는 Dr. Cliff Click 블로그 게시물을 참조하십시오 .


10

D는 공변량 배열을 허용하지 않습니다.

void main()
{
    class Foo {}
    Object[] a = new Foo[10];
}  

/* Error: cannot implicitly convert expression (new Foo[](10LU)) of type Foo[]
to Object[] */

당신이 말했듯이, 이것을 허용하는 타입 시스템의 구멍이 될 것입니다.

이 버그 는 12 월 13 일에 릴리스 된 최신 DMD에서만 수정 되었으므로 실수로 인해 용서받을 수 있습니다 .

D에서의 배열 액세스는 C 또는 C ++에서와 같이 빠릅니다.


d-programming-language.org/arrays.html 에 따르면 "정적 배열 T [dim]은 다음 중 하나로 암시 적으로 변환 될 수 있습니다. ... U [] ... 동적 배열 T []는 암시 적으로 변환 될 수 있습니다 U [] ... 여기서 U는 T의 기본 클래스입니다. "
Ben Voigt

1
@ BenVoigt : 오래된 문서입니다.
BCS

1
@ BenVoigt : 문서를 업데이트하기 위해 풀 요청을 만들었습니다. 잘만되면 이것은 곧 해결 될 것입니다. 지적 해 주셔서 감사합니다.
피터 알렉산더

5

저렴한 랩톱에서 수행 한 테스트에서 사용 int[]과 의 차이점 Integer[]은 약 1.0ns입니다. 유형에 대한 추가 검사로 인해 차이가 발생할 수 있습니다.

일반적으로 객체는 모든 ns 카운트가 아닌 경우에만 높은 수준의 논리에 사용됩니다. 모든 ns를 저장 해야하는 경우 Objects와 같은 더 높은 수준의 구문을 사용하지 마십시오. 과제만으로는 실제 프로그램에서 매우 작은 요소 일 수 있습니다. 예를 들어 같은 머신에서 새 객체를 생성하는 것은 5ns입니다.

String과 같은 복잡한 객체를 사용하는 경우 compareTo에 대한 호출은 훨씬 비쌉니다.


2

다른 현대 OO 언어에 대해 물었습니까? 델파이 string는 객체가 아니라 기본 요소가되어이 문제를 완전히 피 합니다. 따라서 문자열 배열은 정확히 문자열 배열이며 다른 것은 없으며, 형식 검사 오버 헤드없이 네이티브 코드보다 빠릅니다.

그러나 문자열 배열은 자주 사용되지 않습니다. 델파이 프로그래머들은 TStringList수업 을 선호하는 경향이 있습니다. 최소한의 오버 헤드로 인해 클래스가 스위스 군용 나이프와 비교 될 정도로 많은 상황에서 유용한 일련의 문자열 그룹 방법을 제공합니다. 따라서 문자열 배열 대신 문자열 목록 객체를 사용하는 것이 관용적입니다.

다른 객체의 경우 C ++ 에서처럼 델파이에서 배열이 공변량이 아니기 때문에 여기에 설명 된 종류의 시스템 구멍을 막기 때문에 문제가 없습니다.


1

또는 예를 들어, Quicksort가 항상 O (n log n) 불필요한 런타임 검사를 수행한다는 사실을 알고 살아야합니까?

CPU 성능은 단조롭지 않습니다. 즉, 더 긴 프로그램이 더 짧은 프로그램보다 빠를 수 있습니다 (CPU에 따라 다르며 일반적인 x86 및 amd64 아키텍처에 해당). 따라서 배열에서 바운드 검사를 수행하는 프로그램이 이러한 바운드 검사를 제거하여 이전에서 추론 한 프로그램보다 실제로 더 빠를 수 있습니다.

이 동작의 이유는 바운드 검사가 메모리의 코드 정렬을 수정하고 캐시 적중 빈도 등을 수정하기 때문입니다.

그렇습니다. Quicksort는 항상 O (n log n) 스퓨리어스 검사를 수행하고 프로파일 링 후 최적화한다는 사실을 알고 있습니다.


1

스칼라는 공변량 배열이 아닌 변하지 않는 배열을 가진 OO 언어입니다. JVM을 대상으로하므로 성능상의 이점이 없지만 Java 및 C #에 공통적 인 기능을 피하여 이전 버전과의 호환성으로 인해 유형 안전성을 손상시킵니다.

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