출력해야 할 파일 개수가 아주 많을 경우...

solarsys의 이미지

맨날 눈팅만 하다 질문 하나 올려 봅니다.

계산 결과를 저장할 파일의 개수가 아주 많을 때에 파일 출력에 소요되는 시간을 줄이려면 어떻게 하는 게 좋은지 조언 부탁드립니다.

제가 짠 코드는 아래와 같습니다.

n = 10000;
do {
    ct += dt;
    수치계산함수(n, ct, ..., r);
    for (q = 1; q <= n; q++) {
        sprintf(fname, "./out%05d.dat", q);
        OUT = fopen(fname, "at");
        fprintf(OUT, "%.2lf %+.09e %+.09e %+.09e\n", ct, r[q][1], r[q][2], r[q][3]);
        fclose(OUT);
    }
} while (ct <= ct_max)

이렇게 하니, 계산에 걸리는 시간보다 파일 출력에 소요되는 시간이 훨씬 더 길더군요.

####

본문 정정:

제가 좀 착각했습니다.

다시 확인해 보니, 계산 자체에 걸리는 시간이 훨씬 더 길었습니다.

계산 결과를 이진파일 하나에 몽땅 때려넣어도 봤는데, 10000개에 흩트려 저장하는 것과 별반 차이가 없었습니다.

parkon의 이미지

dt-loop이 돌때마다 fopen과 fclose가 반복되어 사용되는 군요.
파일 포인터 어레이를 만드시면 그 반복을 피할 수 있어 속도가 좀 빨라질듯 합니다.

예를 들어 n이 상수라면,

const int n = 10000; 
FILE *outs[n+1];
 
for (int q = 1; q <= n; q++) {
        sprintf(fname, "./out%05d.dat", q);
        outs[q]= fopen(fname, "wt");
}
 
do {
    ct += dt;
 
    수치계산함수(n, ct, ..., r);
 
    for (int q = 1; q <= n; q++) {
        fprintf(outs[q], "%.2lf %+.09e %+.09e %+.09e\n", ct, r[q][1], r[q][2], r[q][3]);
    }
} while (ct <= ct_max);
 
for (int q = 1; q <= n; q++) {
        fclose(outs[q]);
}

대충 이런식으로요.
solarsys의 이미지

알려 주신 방법을 적용해 보니,
하드디스크 드르륵거리는 소리가 확 줄어들었고, 총 소요 시간도 40%가량 단축되었습니다.

gnuplot를 써서 10000개 파일에 저장된 좌표를 모두 겹쳐서 그려 보았습니다(아래 첨부 파일).

감사합니다.

댓글 첨부 파일: 
첨부파일 크기
Image icon mp_10k-1.k145n.xy_.png921.22 KB
ymir의 이미지

매번 파일을 열고 쓰던, 미리 파일을 열어 놓던...
전체적으로 수행해야 하는 오퍼레이션은 변함이 없는데..
순서만 바꾸는 것으로도 그만한 효과를 봤다니.. 조금 희한하네요.

10k 라면 OPEN_MAX 에 걸려서 생성 안 된 파일이 있을 수도 있습니다.
별도로 커널 수정 안 했다면.. hard limit 이 4k, soft limit 이 1k 정도 되었던것 같은데..
한 번 확인해 보시는 것도 좋을 것 같네요.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

solarsys의 이미지

모든 파일(개당 24킬로바이트짜리 10000개)이 정상적으로 생성되었습니다.

10000개 파일을 매번 열고 닫으면서 저장해 둔 결과와 맞춰 보았는데,
모든 게 정확히 일치했습니다.

한 번 계산하는 데 3분 30초가량 걸리던 것이 2분 30초대로 줄었고,
줄어든 시간은 순수한 계산 시간이 아니라 파일 쓰기에 걸린 시간이었습니다.

아무래도 매번 열고 닫는 것보다는
전부 한꺼번에 열어 놓은 다음에 데이터를 추가하는 게 당연히 더 빠르지 않을까요?

열 수 있는 파일의 최대 개수는, ulimit -n으로 확인해 보니, 16384개였습니다.

ymir의 이미지

피드백 고맙습니다.

-- 추가 정정 --
음.. 코드를 다시 자세히 보니.. 같은 파일에도 계속 내용을 추가하는 것이었군요.
호출 횟수가 똑같은게 아니었네요. 제가 착각했습니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

goforit의 이미지

메모리가 충분하면 중간버퍼로 파일을 쓰는 곳을 tmpfs 를 사용해보시는 것을 어떨까요?
디렉토리를 하나 만들고 tmpfs를 만들어쓰면 메모리에 쓰는 것과 비슷한 속도입니다.

solarsys의 이미지

예전 도스 시절 램디스크를 써 본 적이 있는데, 리눅스의 tmpfs라는 것도 그것과 같은 개념이라 보면 되겠군요.
좋은 정보 감사합니다.

mirheekl의 이미지

만약 해당 계산함수가 싱글 스레디드로 구현되어 있고 다중 CPU를 사용하는 환경이라면, 계산결과를 별도의 스레드에 넘겨 저장하게 하고 원래의 스레드는 곧바로 다른 계산을 시작하는 방식으로 좀더 퍼포먼스 향상을 꾀할 수 있을 것 같습니다.

--

solarsys의 이미지

오늘 중으로 시도해 보겠습니다.

OpenMP 지시어를 몇 줄 추가하면 될 것 같기도 합니다.

감사합니다.

mirheekl의 이미지

OpenMP로 이득을 볼수도 있지만 오히려 악영향을 미칠 수도 있습니다. (물론 쉽게 테스트 가능하다면 시도해볼 가치가 충분합니다.)

다만 제가 추천드렸던 것은 OpenMP사용이 아니고, 그저 파일 저장 작업만 별도의 스레드를 통해 처리하는 방법이었습니다. 만약 수치계산 함수가 싱글스레디드라면 파일 저장 작업에 소요되는 시간을 대폭 아낄 수 있습니다. 조건만 만족한다면 거의 파일 저장에 들어가는 추가시간을 0에 수렴하게 만들 수도 있겠네요.

--

solarsys의 이미지

오히려 시간이 더 걸렸습니다.

파일 쓰기 부분 외에 계산 부분에도 적용해 봤지만 대부분의 경우 시간이 더 걸렸고,
계산 시간이 줄어든 경우라도 그 효과는 아주 작았습니다.

멀티스레딩 프로그래밍은 처음 해 보는 거라 제대로 되고 있는 건지 모르겠는데,
이것도 역시 시간상으로 별 이득이 안 되는 듯합니다.

차라리 작업을 이등분 혹은 삼등분 해서 동시에 따로 돌리는 게 더 나을지도 모르겠습니다.

일단 전에 하던 대로 순차적으로 처리하되, 계산 결과를 매번 저장하지 않고 열 번 혹은 백 번에 한 번씩만 저장하도록 바꿨습니다.

mirheekl의 이미지

위에 적었다시피 OpenMP를 사용하시라는 의미가 아니었고, 그저 출력부만 별도의 스레드로 분리하라는 뜻. 이건 OpenMP와 아무런 관계가 없습니다.

그리고 이 방법은 당연한 얘기지만 계산 시간 자체는 전혀 줄어들지 않습니다. 계산이 진행되는 동안 놀고 있는 자원을 이용해 그 전의 계산결과를 저장하자는 취지이니까요.

따라서 싱글코어이든지 연산함수 자체가 멀티스레딩이든지 기타등등의 이유로 계산함수가 돌아가는 동안 가용자원이 생기지 않는다면 의미가 없습니다.

직접 만나서 동작환경을 살펴보며 설명을 드릴 수 있으면 좋을텐데 그럴 수가 없어 아쉽네요

--

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.