2009-10-11 3 views
4

이 질문은 단지 추측입니다. 내 상자에C++ 및 Java 성능

using namespace std; 

void testvector(int x) 
{ 
    vector<string> v; 
    char aux[20]; 
    int a = x * 2000; 
    int z = a + 2000; 
    string s("X-"); 
    for (int i = a; i < z; i++) 
    { 
    sprintf(aux, "%d", i); 
    v.push_back(s + aux); 
    } 
} 

int main() 
{ 
    for (int i = 0; i < 10000; i++) 
    { 
    if (i % 1000 == 0) cout << i << endl; 
    testvector(i); 
    } 
} 

는,이 프로그램은 약에서 실행됩니다 :

나는 C++에서 다음과 같은 구현을 가지고있다. 12 초; 놀랍게도, 비슷한 구현을 Java [String 및 ArrayList 사용]하고 내 C++ 응용 프로그램 (약 2 초)보다 훨씬 빠르게 실행됩니다. ..., 너무 ++ C에서 구현 될 수 있도록

내가 네이티브로 변환 할 때 자바 핫스팟이 최적화를 많이 수행 알고 있지만, 이러한 성능은 자바로 할 수 있으면 나는 생각한다, 당신을 무엇 위의 프로그램에서 수정해야한다고 생각하거나, 사용 된 라이브러리 나 메모리 할당 자에서 이와 비슷한 퍼포먼스에 도달해야한다고 생각합니까? (이러한 것들의 실제 코드를 작성하는 것은 매우 길 수 있으므로 그것에 대해 논의하는 것이 좋습니다) ...

감사합니다.

+4

코드가 컴파일되지 않는 것 같다. 이게 뭐야? – UncleBens

+0

@UncleBens - 필요한'include' 지시문을 추가하십시오. –

+0

int를 벡터 <**string**>으로 푸시하려고 시도하기 때문에 컴파일되지 않습니다. 일치하는 testvector (void)가 없습니다. n 개의 문자열 사본을 저장하는 경우 Java는 동일한 문자열에 대한 n 개의 참조를 저장한다고 가정합니다. 그 (것)들로 아무 것도 이제까지 끝나지 않기 때문에, 당신은 동일한 끈에 또한 포인터를 저장할 수 있었다 :'string s ("X-"); 벡터 (x, &s);'매우 빠를 것 같네요 :) – UncleBens

답변

12

성능 테스트에주의해야합니다. 속임수를 쓰는 것이 매우 쉽거나 비슷하지 않기 때문입니다.

그러나 C#과 C++를 비교 한 비슷한 결과를 보았습니다. 그리고 이런 종류의 증거에 직면했을 때 네이티브 코더의 놀라움에 대한 유명한 블로그 게시물이 많이 있습니다. 근본적으로 좋은 세대의 압축 GC는 많은 작은 할당을 위해 훨씬 더 최적화되어 있습니다.

C++의 기본 할당 자에서 모든 블록은 동일하게 취급되므로 할당 및 해제하는 데 평균적으로 비용이 많이 듭니다. generational GC에서 모든 블록은 할당하기 매우 저렴하고 (스택 할당만큼 저렴합니다.) 수명이 짧다는 것을 알게되면 정리하기가 매우 저렴합니다.

이 때문에 C++의 "빠른 성능"이 현대 언어에 비해 - 대부분 - 신화 적입니다. C++이나 Java 프로그램의 성능에 맞설 수 있기 전에 모든 C++ 프로그램을 손에서 조정해야합니다.

+1

'모든 조정에서 수동 조정'이란 메모리 풀에서 할당하는 사용자 정의 할당자를 사용하는 것입니까? – gnud

+5

바로 가기 풀 할당 자와 마찬가지로? 거의 동일한 행을 하나씩 변경하여 동일한 성능을 얻습니다. – GManNickG

+3

@gnud - 직접 비교를 시도하십시오. 풀에서 서브 할당하여 이미 malloc을 구현했기 때문에 사용자 정의 풀 할당자를 삭제하면 대부분의 구현에서 실망스러운 차이가 있습니다. GC 및 JIT 언어는 도달 할 수없는 객체를 식별하기 위해 함수 호출의 스택 레이아웃에 대한 정보를 활용할 수 있으며 조각을 없애기 위해 객체를 이동할 수 있으며 과중한 작업 부하가 발생할 때까지 값 비싼 작업을 지연시킬 수 있습니다. 포인터 기반 메모리 조작은 최적화하기가 비교적 어렵습니다. –

1

요소의 공간을 루프 이전에 v에 예약하는 데 도움이됩니다 (단, 같은 코드로도이 코드의 속도를 높여야합니다).

0

여기 실제로 측정하려는 것은 무엇입니까? int를 벡터에 두는 것?

당신은 벡터의 노하우 크기 벡터에 사전 할당 공백으로 시작할 수 있습니다

대신 :

void testvector(int x) 
{ 
    vector<string> v; 
    int a = x * 2000; 
    int z = a + 2000; 
    string s("X-"); 
    for (int i = a; i < z; i++) 
    v.push_back(i); 
} 

시도 : 당신의 내부 루프에서

void testvector(int x) 
{ 
    int a = x * 2000; 
    int z = a + 2000; 
    string s("X-"); 
    vector<string> v(z); 
    for (int i = a; i < z; i++) 
    v.push_back(i); 
} 
+0

벡터를 미리 할당하는 것은 모든 문자열 할당과 비교할 때 상당히 중요하지 않습니다. – dhardy

0

int를 문자열 벡터로 푸시합니다. 머신 코드 레벨에서 단일 단계 만 수행한다면, 많은 시간이 문자열 할당 및 포맷팅으로 이어지고, 약간의 시간이 푸시 백 (push back)에 들어간다는 것을 알았을 것입니다. 벡터).

이것은 개발자가 사람들이 합리적으로 무엇을하고 싶은지에 따라 런타임 라이브러리 구현간에 쉽게 다를 수 있습니다.

+0

죄송합니다. 여기에 프로그램을 작성하는 동안 오류가 발생했습니다. 지금 해결되었습니다. – ebasconp

+1

@ebascomp : 맞습니다.하지만 이제는 내부 루프에 s + i가 있습니다. 무해한 것처럼 보이지만 분해 단계에서 한 단계 씩 진행하면 많은 일을하게 될 것입니다. 알려주는 또 다른 방법은 디버거에서 일시 중지하는 것입니다. 나의 추측은's + i'를 실행하는 과정에서 90 % 이상이된다. –

+1

불필요한 문자열 추가를 제거 했으므로 약 절반의 시간이 sprintf()에서 소비되고 절반은 std :: string을 생성합니다. std :: string은 그리 효율적이지 않으므로 Java는 초당 속도가 빠를 수도 있습니다. 나는 sprintf()의 비용을 줄일 수 있지만 모호하다. –

1

C++과 java의 성능이 서로 다른 이유를 제안하려면 C++에서 여러 가지 성능 문제를 볼 수 있습니다. 일부 경우에는 동일하게 처리하는지 확인하는 것이 유용 할 수 있습니다. java (예 : std :: endl을 통해 출력 스트림을 플러시하는 경우 System.out.flush()를 호출하거나 나중에 \ n을 추가하십시오.

+0

Java 코드를 보는 것은 흥미로울 것입니다.하지만 플러시를 불필요하게 10 번 호출하면 성능에 어떤 차이가 생길지 모릅니다. – UncleBens

+0

이것은 단 한 가지 예일 뿐이지 만 출력을 플러시하는 것은 커다란 영향을 미칠 수 있습니다. 플러싱 데이터는 일반적으로 블로킹을 의미하며 쓰여지는 (버퍼링 된 스트림)은 곧바로 되돌아옵니다. – vickirk

4

핫스팟은 코드의 핫 스폿을 최적화합니다. 일반적으로 10000 번 실행되면 최적화를 시도합니다.

이 코드의 경우 5 회 반복 한 후에 벡터에 문자열을 추가하는 내부 루프를 최적화하려고합니다. 가능성보다 더 많이 수행 할 최적화는 메소드의 변수 분석에서 탈출을 포함합니다. 벡터는 로컬 변수이며 결코 로컬 컨텍스트를 이스케이프하지 않습니다. 메서드의 모든 코드를 제거하고 아무 작동도하지 않게 할 가능성이 큽니다. 이를 테스트하려면 메서드에서 결과를 반환하십시오. 그렇더라도 결과에 의미있는 것을 수행하도록 조심하십시오. 예를 들어 길이를 얻는 것은 호르 스포트가 결과를 루프의 반복 횟수와 동일하게 볼 수 있으므로 최적화 할 수 있습니다.

이 모든 것은 핫스팟과 같은 동적 컴파일러의 주요 이점을 나타냅니다. 런타임 분석을 사용하면 런타임에 실제로 수행되는 작업을 최적화하고 중복 코드를 제거 할 수 있습니다. 결국, 사용자 지정 C++ 메모리 할당자가 얼마나 효율적인지는 중요하지 않습니다. 코드 실행이 항상 빠를 것입니다.

6

귀하의 모든 프로그램은 0..9000을 1000 단계로 인쇄합니다. testvector()에 대한 호출은 아무 것도하지 않으며 제거 될 수 있습니다. 나는 당신의 JVM이 이것을 알아 차리고 모든 기능을 근본적으로 최적화하고 있다고 생각한다.

testvector()에 대한 호출을 주석 처리하여 C++ 버전에서도 비슷한 효과를 얻을 수 있습니다!

+1

"while (true)"와 그 본문이 없기 때문에 Java가 0 초 만에 무한대를 완료 할 수 있다고 결론을 내린 "벤치 마크"와 같습니다 : P – JulianR

3

내 상자에는이 프로그램이 약 실행됩니다. 12 초; 놀랍게도, 비슷한 구현을 Java [String 및 ArrayList 사용]하고 내 C++ 응용 프로그램 (약 2 초)보다 훨씬 빠르게 실행됩니다.

그 결과를 재현 할 수 없습니다.

Alex가 언급 한 최적화를 설명하기 위해 Java 및 C++ 코드가 testvector 메서드 끝에있는 v 벡터의 마지막 결과를 인쇄하도록 코드를 수정했습니다.

이제 C++ 코드 (-O3으로 컴파일 됨)가 대략 12 초만큼 빠르게 실행됩니다. Java 코드 (Vector 대신에 ArrayList을 사용합니다. 이스케이프 분석 덕분에 성능에 영향을 미칠 것으로 의심됩니다)은 약 2 배의 시간이 소요됩니다.

나는 이 아니며,이 결과가 중요하지 않으므로 많은 테스트를 수행합니다. 이 테스트를 완전히 잘못 수행하는 것이 얼마나 쉬운 지와 실제 성능에 대해 말할 수있는 단일 테스트가 얼마나 적은지를 보여줍니다.

그냥 기록을 위해, 시험은 다음과 같은 구성에서 실행되었다 :

$ uname -ms 
Darwin i386 
$ java -version 
java version "1.6.0_15" 
Java(TM) SE Runtime Environment (build 1.6.0_15-b03-226) 
Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-92, mixed mode) 
$ g++ --version 
i686-apple-darwin9-g++-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5490) 
+0

어떤 운영 체제에서 Java 버전을 사용 했습니까? – Jesper

+0

@Jesper : 저는 MacBook의 MacOSX와 amd64 상자의 쿠분투 9.04에서 Sun JVM 6.0을 사용했습니다. – ebasconp

+0

@Jesper : 좋은 의견입니다. 업데이트를 참조하십시오. –

5

음, 이것은 단지 작은 물체의 할당을 측정하는 꽤 쓸모 테스트입니다. 즉, 간단한 변경으로 인해 실행 시간이 약 15 초에서 약 4 초로 줄어 들었습니다.새로운 버전 :

real 0m2.923s 
user 0m2.490s 
sys  0m0.063s 

(이 쓸모 할당 줄이려고 매개 변수로 ArrayList를 전달 제외하고 이것은, 원래 프로그램의 내 직접 자바 포트) :

typedef vector<string, boost::pool_allocator<string> > str_vector;  

void testvector(int x, str_vector::iterator it, str_vector::iterator end) 
{ 
    char aux[25] = "X-"; 
    int a = x * 2000; 
    for (; it != end; ++a) 
    { 
     sprintf(aux+2, "%d", a); 
     *it++ = aux; 
    } 
} 

int main(int argc, char** argv) 
{ 
    str_vector v(2000); 
    for (int i = 0; i < 10000; i++) 
    { 
     if (i % 1000 == 0) cout << i << endl; 
     testvector(i, v.begin(), v.begin()+2000); 
    } 
    return 0; 
} 

real 0m4.089s 
user 0m3.686s 
sys  0m0.000s 

자바 버전은 시간을 가지고 .

요약하면 Java에서 작은 할당이 더 빠르며 메모리 관리는 C++에서 좀 더 번거로운 작업입니다. 하지만 우리는 이미 그것을 알고있다.

+0

당신은 또한 sprintf를 제거하려고 할 수 있습니다, 그것은 내 테스트에서 거대한 시간 싱크대였습니다. – rpg

+0

원본에 사용 된 바보 같은 문자열 개체를 제거하고 스택에 하나의 char 버퍼를 사용합니다. 또한 접두어가 아닌 매 번만 덮어 씁니다. 나는 sprintf가 sstream보다 훨씬 나쁘다고 생각하지 않습니다. 문제는 문자열 연결의 원래 구현이었습니다. – gnud

+0

한순간에 나는 벡터와 std :: string을 완전히 제거했고 나는 sprintf가 얼마나 느린 지 믿을 수 없었다. 어쩌면 이것은 MSVC 9에만 해당 될 것입니다. – rpg