2012-02-11 1 views
2

가능한 가장 빠른 런타임을 위해 C++ 프로그램을 최적화하는 데 문제가 있습니다.C++에서 입출력 최적화

코드의 요구 사항은 파일을 통해 프로그램으로 공급되는 2 개의 long 정수의 차이의 절대 값을 출력하는 것입니다. 예 :

파일 이름은 알 수 없으며 한 줄에 2 개의 숫자가 공백으로 구분되어 있습니다. 알 수없는 양의 테스트 데이터가 있습니다. 테스트 데이터 2 파일을 만들었습니다. 하나는 극단적 인 경우를 가지며 5 회 길다. 다른 하나는 Java 프로그램을 사용하여 2,000,000 개의 난수를 생성하고이를 timedrun 파일에 출력했습니다. 18.MB 상당의 테스트가있었습니다.

거대한 파일은 3.4 초에 실행됩니다. 1.1 초로 줄여야합니다.

int main() { 
long int a, b; 
while (scanf("%li %li",&a,&b)>-1){ 
    if(b>=a) 
    printf("%li/n",(b-a)); 
    else 
    printf("%li/n",(a-b)); 
    } //endwhile 
return 0; 
}//end main 

내 프로그램에 Valgrind의를 실행하고, 보류 업을 많이 읽기에 있었고 일부를 쓰기 것을 보여 주었다 :

이 내 코드입니다. 제가 전화를받는 것만 알면 어떻게 인쇄/스캔을 가장 원시적 인 형태의 C++로 다시 작성할 수 있습니까? 숫자를 이진수로 스캔하여 논리적 연산으로 데이터를 조작하여 차이를 계산할 수있는 방법이 있습니까? 나는 또한 버퍼를 작성하는 것을 고려해야한다고 들었지만, 웹을 검색하고 코드를 시도한 지 약 6 시간이 지난 후에 나는 실패했다.

도움을 주시면 감사하겠습니다.

+0

최적화로 컴파일 했습니까? – Dave

+0

출력을 리디렉션합니다. –

+1

프로그램이 파일을 읽지 않습니다. 리디렉션을 사용하여 파일 내용을 프로그램에 제공하고 있습니다. 응용 프로그램에서 직접 파일을로드하고 메모리에 전체를로드 한 다음 scanf보다 낮은 수준의 루틴을 찾아 파싱합니다. –

답변

2

당신이해야 할 일은 반복 된 I/O 호출을하기보다는 전체 문자열을 메모리에로드 한 다음 거기에서 숫자를 추출하는 것입니다. 그러나 잘 알면 하드 드라이브에서 18MB를로드하는 데 많은 시간이 걸리는 것입니다.

+2

합리적으로 현대적인 하드 드라이브는 80-120 MB/s 사이에서 읽을 수 있습니다. 이것은 소요 시간을 설명하지 않습니다. –

+0

@BenVoigt : 누가 합리적으로 현대적인 하드 드라이브를 가지고 있다고 말합니까? – Puppy

+0

네 말이 맞아. 그가 성능에 신경을 쓰면 아마도 SATA-II SSD (200 MB/s) 또는 SATA-III SSD (500 MB/s)가있을 것입니다. 또는 핫 파일 시스템 캐시 (여러 GB/s). –

1

파일 형식을 보장 할 수 있기 때문에 scanf를 크게 향상시킬 수 있습니다. 형식이 무엇인지 정확히 알기 때문에 많은 오류 검사가 필요하지 않습니다. 또한 printf는 새 행의 변환을 해당 플랫폼의 해당 행 분리로 수행합니다.

저는이 정수 코드에서 꽤 큰 속도 향상을 얻으려고이 SPOJ forum post에서 발견 된 코드와 비슷한 코드를 사용했습니다 (nosy의 페이지 절반 부분을 참조하십시오). 음수를 처리하려면이 값을 수정해야합니다. 다행히도 더 빠른 printf 함수를 작성하는 방법에 대한 아이디어를 줄 수 있기를 바라지 만, scanf를 대체하는 것으로부터 시작하여 여러분이 얼마나 멀리 떨어져 있는지 보겠습니다.

+0

정수 ='itoa' +'puts'에 대해 더 빠른'printf'가 있습니다. 'itoa'의 최적화 된 버전 [here] (http://stackoverflow.com/questions/4351371/c-performance-challenge-integer-to-stdstring-conversion). –

1

문제는이 모든 숫자를 읽고 텍스트에서 이진으로 변환하는 것이 좋습니다.

가장 좋은 개선점은 이진수로 프로그램을 생성하는 모든 프로그램에서 숫자를 쓰는 것입니다. 이렇게하면 디스크에서 읽어야하는 데이터의 양이 크게 줄어들어 텍스트에서 바이너리로 변환하는 데 필요한 시간이 약간 줄어 듭니다.

당신은 2,000,000 개의 숫자가 18MB = 9 바이트를 차지한다고 말합니다. 여기에는 공백과 끝의 라인 마커가 포함되어 있으므로 합리적이라고 생각합니다.

숫자를 4 바이트 정수로 저장하면 디스크에서 읽어야하는 데이터의 양이 절반이됩니다. 형식 변환을 절약하는 것 외에도 성능을 두 배로 늘리는 것이 합리적입니다.

더 많은 것을 필요로하기 때문에 더 급진적 인 것이 필요합니다. 데이터 파일을 각각의 디스크에있는 별도의 파일로 분할 한 다음 자체 프로세스에서 각 파일을 처리하는 것을 고려해야합니다.4 개의 코어가 있고 프로세싱을 4 개의 개별 프로세스로 분리하고 4 개의 고성능 디스크를 연결할 수 있다면 성능을 두 배로 늘릴 수 있습니다. 이제 병목 현상이 OS 디스크 관리가되고 OS가 4 개의 디스크를 얼마나 효율적으로 관리 하는지를 추측 할 수 없습니다.

나는이 작업이 상당히 단순화 된 모델이라고 가정합니다. 당신의 설명이 전부라면, 실제 해결책은 테스트 파일을 작성하는 프로그램에서 뺄셈을하는 것입니다!

+0

"최고의 개선 [...] 숫자로 [...] 작성 바이너리." <--- 이것. (글쎄, 이것과 메모리는 파이프를 통해 모든 것을 푸시하지 않고 파일을 맵핑합니다). – Damon

0

프로그램에서 파일을 열고 한꺼번에 읽는 것보다 더 나은 방법은 메모리 매핑입니다. ~ 18MB는 프로그램에서 사용할 수있는 ~ 2GB의 주소 공간에서 문제가되지 않습니다.

그런 다음 strtod을 사용하여 숫자를 읽고 포인터를 앞으로 내리십시오.

나는 입력 리디렉션에 비해 5-10 배의 속도 향상과 scanf을 기대합니다.