• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:処理が異常に遅い原因はわかりますか?)

処理が異常に遅い原因はわかりますか?

このQ&Aのポイント
  • 久しぶりに、(perlのXSのために)C言語を組んでみましたが、以下のコードが、異常に遅いようでした。
  • 例えでいえば、同じことをする数文字のperlの正規表現処理の数100倍の時間がかかっていました。
  • どこが、原因でしょうか?以下のコードは、きちんと爆速で動作してくれます。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.6

No.5の補足です。 各CPU向けの最適化について、VC++10のmemcpyについて言えば CPUがSSE2をサポートしており、コピー元とコピー先のメモリアライメントが 16バイトで揃っていればSSE命令を利用し、それ以外でもストリング命令による コピーが行われるなど単純にジャンプ命令でループしながらコピーするよりも 高速にメモリ転送が行われるように出来ています。 私の環境ではSSE2を使っても使わなくても0.5秒以下くらいでした。 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CopyUp: ; ; First, see if we can use a "fast" copy SSE2 routine ; block size greater than min threshold? cmp ecx,080h jb Dword_align ; SSE2 supported? cmp DWORD PTR __sse2_available,0 je Dword_align ; alignments equal? push edi push esi and edi,15 and esi,15 cmp edi,esi pop esi pop edi jne Dword_align ; do fast SSE2 copy, params already set jmp _VEC_memcpy ; no return ; ; The algorithm for forward moves is to align the destination to a dword ; boundary and so we can move dwords with an aligned destination. This ; occurs in 3 steps. ; ; - move x = ((4 - Dest & 3) & 3) bytes ; - move y = ((L-x) >> 2) dwords ; - move (L - x - y*4) bytes ; Dword_align: test edi,11b ;U - destination dword aligned? jnz short CopyLeadUp ;V - if we are not dword aligned already, align shr ecx,2 ;U - shift down to dword count and edx,11b ;V - trailing byte count cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindUp ;V - if so, then jump rep movsd ;N - move all of our dwords jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (5)

回答No.5

ymda さんコード提供ありがとうございます。 gcc4.2 -O3だとインデックス参照になっていて、llvm-gccだと内側のループが memcpyになっており外側のループがカウントダウンになっています。 llvm-gccだと、最適化でかなりコードが書き換えられているようです。 memcpyはコンパイラによっては、自作関数より遅いことがあるそうですが、 大抵は各CPU向けの最適化が施されており速くなるように作られています。 インテルとかのコンパイラは各CPU向けの最適化が行われるので速く gccは多様な環境に対応することを第一として設計されているので 個別のCPU向けの最適化はあまりないようです。 結局コンパイラ次第ってことですかね…

全文を見る
すると、全ての回答が全文表示されます。
回答No.4

No.2 さんのアセンブラ出力を見る限り、 func1はd++やs++をせずにiによるインデックス参照になっており、 func2はループ自体回らず何もせずreturnするようになっており、 func3はソースコード通りのポインタ直参照となっております。 なので、func1よりfunc3の方が速くfunc2は何もしていないので 爆速かと思われます。 あと、fun1のcmpl %eax, %edxよりもfunc2のtestb %al, %alの方が 若干速いと思われるのでそのあたりも関係があるかもしれません。 ymda さんの環境で出力したアセンブラコードがあればもう少し 詳しく分かるかもしれません。

ymda
質問者

お礼

ありがとうございます。 他のコンパイラ(バージョン)も出してみました。 しかし、下のやりすぎて処理は消えている以外は、それなりに時間がかかる処理ですので、かかるのは当然ですが (とはいっても、0.5秒もかかりません) その処理が、gcc4.2 (-O3)ですと、20秒以上待たされるようです。 ちなみに、どのコンパイラも、-Oなしですと、20秒以上待たされる程激遅になるのは、確かなようです。 # ↓だけは、さすがに、0.0秒です。 /********* gcc4.8 -O3 (やりすぎて処理が消えている) .file "test.c" .section .text.startup,"ax",@progbits .p2align 4,,15 .globl main .type main, @function main: .LFB0: .cfi_startproc rep; ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (FreeBSD Ports Collection) 4.8.0 20120930 (experimental)" .section .note.GNU-stack,"",@progbits /********* gcc4.8 -O3 fputcを追加 .file "test.c" .section .text.startup,"ax",@progbits .p2align 4,,15 .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %r13 .cfi_def_cfa_offset 16 .cfi_offset 13, -16 movl $100000, %edi movl $100000, %r13d pushq %r12 .cfi_def_cfa_offset 24 .cfi_offset 12, -24 pushq %rbp .cfi_def_cfa_offset 32 .cfi_offset 6, -32 pushq %rbx .cfi_def_cfa_offset 40 .cfi_offset 3, -40 subq $8, %rsp .cfi_def_cfa_offset 48 call malloc movl $100000, %edi movq %rax, %rbp call malloc movq %rax, %r12 .p2align 4,,10 .L2: xorl %ebx, %ebx .p2align 4,,10 .L5: movl $97, %edi call fputc movzbl (%r12,%rbx), %edx movb %dl, 0(%rbp,%rbx) addq $1, %rbx cmpq $100000, %rbx jne .L5 subl $1, %r13d jne .L2 addq $8, %rsp .cfi_def_cfa_offset 40 popq %rbx .cfi_def_cfa_offset 32 popq %rbp .cfi_def_cfa_offset 24 popq %r12 .cfi_def_cfa_offset 16 popq %r13 .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (FreeBSD Ports Collection) 4.8.0 20120930 (experimental)" .section .note.GNU-stack,"",@progbits

ymda
質問者

補足

/********* 書き直したソース #define AA 100000 #include <stdlib.h> int main(void) { int i,j; int len=AA; char *dst,*savedst; char *src,*savesrc; dst=malloc(AA); src=malloc(AA); savesrc=src; savedst=dst; for(j = 0; j < AA; j++) { src=savesrc; dst=savedst; for(i = 0; i < len; i++) { //fputc('a'); *dst++=*src++; } } } /********* gcc4.2 -O3 (-O2も全く同じ) .file "test.c" .text .p2align 4,,15 .globl main .type main, @function main: .LFB2: pushq %rbx .LCFI0: movl $100000, %edi call malloc movl $100000, %edi movq %rax, %rbx call malloc xorl %esi, %esi movq %rax, %rcx .L2: xorl %edx, %edx .p2align 4,,7 .L3: movzbl (%rdx,%rcx), %eax movb %al, (%rdx,%rbx) addq $1, %rdx cmpq $100000, %rdx jne .L3 addl $1, %esi cmpl $100000, %esi jne .L2 popq %rbx ret .LFE2: .size main, .-main .section .eh_frame,"a",@progbits .Lframe1: .long .LECIE1-.LSCIE1 .LSCIE1: .long 0x0 .byte 0x1 .string "zR" .uleb128 0x1 .sleb128 -8 .byte 0x10 .uleb128 0x1 .byte 0x3 .byte 0xc .uleb128 0x7 .uleb128 0x8 .byte 0x90 .uleb128 0x1 .align 8 .LECIE1: .LSFDE1: .long .LEFDE1-.LASFDE1 .LASFDE1: .long .LASFDE1-.Lframe1 .long .LFB2 .long .LFE2-.LFB2 .uleb128 0x0 .byte 0x4 .long .LCFI0-.LFB2 .byte 0xe .uleb128 0x10 .byte 0x83 .uleb128 0x2 .align 8 .LEFDE1: .ident "GCC: (GNU) 4.2.1 20070831 patched [FreeBSD]" .section .note.GNU-stack,"",@progbits /********* llvm-gcc .file "test.c" .text .globl main .align 16, 0x90 .type main,@function main: .Leh_func_begin0: pushq %r15 .Ltmp0: pushq %r14 .Ltmp1: pushq %rbx .Ltmp2: .Ltmp3: movl $100000, %edi callq malloc movq %rax, %rbx movl $100000, %edi callq malloc movl $100000, %r14d movq %rax, %r15 .align 16, 0x90 .LBB0_1: movq %rbx, %rdi movq %r15, %rsi movl $100000, %edx callq memcpy decl %r14d jne .LBB0_1 popq %rbx popq %r14 popq %r15 ret .Ltmp4: .size main, .Ltmp4-main .Leh_func_end0: .section .eh_frame,"a",@progbits .LEH_frame0: .Lsection_eh_frame0: .Leh_frame_common0: .Lset0 = .Leh_frame_common_end0-.Leh_frame_common_begin0 .long .Lset0 .Leh_frame_common_begin0: .long 0 .byte 1 .asciz "zR" .byte 1 .byte 120 .byte 16 .byte 1 .byte 3 .byte 12 .byte 7 .byte 8 .byte 144 .byte 1 .align 8 .Leh_frame_common_end0: .Lmain.eh: .Lset1 = .Leh_frame_end0-.Leh_frame_begin0 .long .Lset1 .Leh_frame_begin0: .Lset2 = .Leh_frame_begin0-.Leh_frame_common0 .long .Lset2 .long .Leh_func_begin0 .Lset3 = .Leh_func_end0-.Leh_func_begin0 .long .Lset3 .byte 0 .byte 4 .Lset4 = .Ltmp0-.Leh_func_begin0 .long .Lset4 .byte 14 .byte 16 .byte 4 .Lset5 = .Ltmp1-.Ltmp0 .long .Lset5 .byte 14 .byte 24 .byte 4 .Lset6 = .Ltmp2-.Ltmp1 .long .Lset6 .byte 14 .byte 32 .byte 4 .Lset7 = .Ltmp3-.Ltmp2 .long .Lset7 .byte 131 .byte 4 .byte 142 .byte 3 .byte 143 .byte 2 .align 8 .Leh_frame_end0: .section ".note.GNU-stack","",@progbits .ident "GCC: (GNU) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build)"

全文を見る
すると、全ての回答が全文表示されます。
回答No.3

私はx86系のアセンブラは読めないので、見当違いでしたらスミマセン。 strncpyでコピーしてみてはどうでしょう?

ymda
質問者

お礼

ありがとうございます。 これは、あくまで、ルーチンの一部を掲載したもので 実際は、余裕で標準ライブラリで処理できないものです。

全文を見る
すると、全ての回答が全文表示されます。
  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.2

>例えでいえば、同じことをする数文字のperlの正規表現処理の数100倍の時間がかかっていました。 正規表現そのものには文字列コピーのような機能はないですけど具体的にはどんな処理なんでしょう? またgccのバージョンも書かれた方がよいかと思います。 FreeBSD 9のgcc 4.7.3だと void func1(char *d, char *s, int n) { for (int i = 0; i < n; i++) { *d++ = *s++; } } void func2(char *d, char *s, int n) { for (int i = 0; i < n; i++) { } } void func3(char *d, char *s) { for (;*d++ = *s++;) { } } で出力されるコードはこんな感じです。 .text .p2align 4,,15 .globl func1 .type func1, @function func1: .LFB0: .cfi_startproc xorl %eax, %eax testl %edx, %edx jle .L1 .p2align 4,,10 .L5: movzbl (%rsi,%rax), %ecx movb %cl, (%rdi,%rax) addq $1, %rax cmpl %eax, %edx jg .L5 .L1: rep ret .cfi_endproc .LFE0: .size func1, .-func1 .p2align 4,,15 .globl func2 .type func2, @function func2: .LFB1: .cfi_startproc rep ret .cfi_endproc .LFE1: .size func2, .-func2 .p2align 4,,15 .globl func3 .type func3, @function func3: .LFB2: .cfi_startproc .p2align 4,,10 .L10: movzbl (%rsi), %eax addq $1, %rsi movb %al, (%rdi) addq $1, %rdi testb %al, %al jne .L10 rep ret .cfi_endproc .LFE2: .size func3, .-func3 ほぼ#1の方のおっしゃってる通りですね。

ymda
質問者

お礼

ありがとうございます。 アセンブラリストを出してから、時間とれなくなってしまいました。。汗 FreeBSDのシステムコンパイラ gcc4.2ですと、-O2でも-O3でも この回答の2倍以上程度のコードになってしまっているようですが、 gcc4.8、4.7.3を試したら、それなりの速度に戻ってくれました。 また、わずかながら、gccよりもllvm-gccのが速い感じもしています。

全文を見る
すると、全ての回答が全文表示されます。
回答No.1

gccがどのようなオブジェクトを出すのか解りませんが。 >for(i = 0; i < len; i++) { > /* *dst++=*src++;  ここをなくすと、爆速になる */ >} この後でiを参照していなければ最適化で空のForループなので無くなっている、或いは i =len;に置換されている事が考えられます。 >for(; *dst++ = *src++;); 一旦ポインタをセットすればレジスタ操作のみになるので比較的高速動作すると思われます。 >for(i = 0; i < len; i++) { >*dst++=*src++;  >} iのインクリメント、lenとの比較が付加されますからステップ数で数倍になります。 i,Lenをレジスタ上に持たなければ動作速度では数十倍になると思われます。 考えるよりアセンブリリストを出して比較すれば一目瞭然ですね。

ymda
質問者

お礼

ありがとうございます。 回答遅くなり申し訳ありません。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • C言語です。コレを使ってなにかプログラミングを組んでください

    C言語です。コレを使ってなにかプログラミングを組んでください よろしくお願いします 何でも構いません #include<stdio.h> void str_copy( char dst[], char src[] ) { int i; for (i =0; src[i] != '\0'; i++) { dst[i] = src[i]; } dst[i]= '\0'; }

  • strcpy関数について(\0のコピー)

    '\0'のsrcからdstへのコピーについて考えて見たのですが、 char *strcpy(char *dst,const char *src) { int i=0; for(i=0;src[i]!='\0';i++) dst[i]=src[i]; dst[i]=src[i];←1 return dest; } このプログラムではfor文の真偽値の判定の段階では'\0'がsrcからdstにコピーできていないので1のdst[i]=src[i]でコピーする必要がありますが、以下のようにプログラムを変更すると char *strcpy(char *dst,const char *src) { int i=0; for(i=0;(dst[i]=src[i])!='\0';i++) ; return dest; } (dst[i]=src[i)の代入式の段階でsrcからdstにコピーしその後でfor文の真偽値の判定をするので、真偽値判定前にdst側配列に'\0'が入っていると言う考えでこのプログラムを理解して良いのでしょうか。なにかミスしそうなプログラムですが、考え方として、私の言っている事は正しいでしょうか。宜しく回答願います。

  • c言語 iconv

    msys環境で実行して,指定されたファイルの文字コードをShift-JISに変換して表示するコードを作成しているのですがうまく表示されません.何がいけないのでしょうか.第1引数に変換対象のファイル名,第2引数にファイルの文字コードを指定しています. #include <stdio.h> #include <string.h> #include <iconv.h> int main(int argc, char *argv[]) { iconv_t conv; char src[10000]; char dst[10000]; int src_len = strlen(src); int dst_len = sizeof(dst) - 1; char *buf_in; char *buf_out; FILE *fp; fp = fopen(argv[1], "r"); if(NULL == fp) { printf("ファイルを開けません\n"); } else { while(fgets(src, 10000, fp) != NULL) { buf_in = src; buf_out = dst; /* 変換器を作成 */ conv = iconv_open("Shift-JIS", argv[2]); /* 変換 */ iconv(conv, &buf_in, &src_len, &buf_out, &dst_len); *buf_out = '\0'; /* 終末処理 */ /* 文字コード後の文字列を表示 */ printf("%s\n", dst); } /* 変換器を終了 */ iconv_close(conv); /*ファイルを閉じる*/ fclose(fp); } return 0; }

  • ConvertINetStringについて

    wininetのInternetReadFileでUTF-8のサイトから文字列を受け取ったところ、2バイト文字が文字化けしていました。 ConvertINetStringでUTF-8からShift_JISに変換しようとしていますが上手くいかず困っています。 プログラムは下のようになっています。 DWORD mode = 0, readSize; char src[1024]; BYTE dst[1024]; int srcLen, dstLen; char c[1024]; while( true ) { readSize = 0; ::ZeroMemory(src, sizeof(src)); ::ZeroMemory(dst, sizeof(dst)); // 受信 InternetReadFile( hRequest, src, sizeof(src), &readSize ); if(readSize == 0) break; srcLen = strlen(src); dstLen = sizeof(dst) - 1; // UTF-8からShift_JISへ ConvertINetString(&mode, 65001, 932, src, &srcLen, dst, &dstLen); // BYTEからcharへ for(int i = 0; i < strlen(dst); i++) c[i] = (char)dst[i]; Console::WriteLine("受信:{0}", gcnew String(dst)); } 1.ConvertINetStringでdstに値がちゃんと入らない(「・」みたいなゴミのような文字が入ります) 2.本題とは逸れるのですが、BYTEからcharへの変換はこれでいいのでしょうか? ConvertINetStringが上手くいかない理由が特にわからないので回答をもらえたらありがたいです。

  • ポインタ変数を用いた配列の反転操作。

    strSrcの文字列を逆順にしてstrDstに格納し、 逆順にした文字列を表示するプログラムを自分なりに考えて、 作成したのですが、途中で行き詰ってしまいました。 src=strSrc;とdst=strDst;辺りが上手くいっていないようなので どなたかどのように記述すれば良くなるのかご教授お願いします。 また、他の問題点などもあればどうかご指摘お願いします。 #include <stdio.h> /* 文字列反転関数 引数: char *src : 反転させる文字列 char *dst : 反転させた結果を書き込む文字列 返値: なし */ void reverseStr(char *src, char *dst) { src=strSrc; dst=strDst; /*文字列の末尾を指すまでインクリメント*/ while(src != 0){ src++; } /*『配列strDstの番地の値』を『配列strSrcの番地の値』に上書き*/ while(*dst != 0){ *dst=*src; src--; dst++; } *dst=0; } /* メイン関数 引数: なし 返値: int 正常終了時 0 */ int main(void) { char strSrc[] = "abcdefghijkl" ; char strDst[] = "01234567890123456789" ; reverseStr( strSrc, strDst ) ; printf("%s\n", strDst ) ; return( 0 ); }

  • c言語の文字列の逆順のプログラムがわかりません

    文字列を逆順して出力するプログラミングがわかりません。 #include <stdio.h> #include <string.h> void reverse(char *moji, char *gyaku); int main(void) { char x[30]; char y[30]; puts("文字を入力してください。\n"); scanf("%s", x); reverse(x, y); printf("逆順すると%sです。\n", y); return (0); } void reverse(char *moji, char *gyaku) { int i, len; len = strlen(moji); gyaku = moji + len - 1; for(i = 0; i < len; i ++){ putchar((int)*gyaku); gyaku--; } } 理想とする実行結果は 文字を入力してください。 abcdefg 逆順するとgfedcbaです。 なんですが、 上記のソースを実行すると 文字を入力してください。 abcdefg gfedcba逆順すると(謎の漢字)です。 となります。 どこがおかしいんでしょうか? よろしくおねがいします。

  • ファイルへの書込み処理が異常に遅い

    以下のファイルへの書込みを行なうプログラムの処理速度が極端に遅く困惑しています。 -------------------------------------------------------------------------------- /*  文字列"0,"をファイルに約2MB出力するプログラム  (2000文字で改行、1行毎にfopen&fclose) */ #include<stdio.h> void main( int argc, char *argv[] ) {   FILE *fp;   for( int i = 0; i < 1000; i++ )   {     if( !(fp = fopen( "C:\hogehoge.log", "a" ) ) )     {       exit( 1 );     }     for( int j = 0; j < 1000; i++ )     {       fprintf( fp "0," );     }     fprintf( "\n" );     fclose( fp );   }   exit( 0 ); } -------------------------------------------------------------------------------- 処理時間は5分程度です。 出力する文字列を"0#"に変えると5~6秒で終了します。 カンマが入ると違うのでしょうか? OSはWinXP、コンパイラはVC++7.0です。 原因と解決策をご存知の方、よろしくご教示下さい。

  • C言語の課題で困っています;

    C言語の課題で困ってます; 学校の課題で、キーボードから文字を入力する(最大80文字)。入力された文字列と、入力した文字列を逆順にした文字列を表示する。(malloc,freeを使って作成して下さい) ヒントで #invlude <stdio.h> void main(void){ char *buf; //入力文字列用 char *seq; //入力文字列用コピー用 char *rev; //逆順文字列用 int lec, i; buf =(char*)malloc(81); printf("文字列を入力:"); scanf("%s"buf ); for(i = 0; buf[i] ??? '\0'; i++){ } Ien = i; /* lenに文字列の長さが入る */ seq = ???(??? + 1); /* len+1文字文確保*/ for(i = 0; ???; i++){ seq[0] = buf[0]; } free(buf); rey = ???(len + 1); for(i = 0; < len; i++){ rev[len - i - 1] = seq[i]; } ren[i] ~ '\0'; printd("入力文字列 : %s\n",sep); でたんですが、全然分からなくて足りない部分の答えを教えてもらえると助かります;

  • インラインアセンブラについて

    現在、インラインアセンブラを学んでいるものです。 開発環境は Visual Studio 2005 です。 プログラムは、unsigned char型の配列を3つ(src1, src2, ans)用意し、src1[i] + src2[i] = ans[i] というようにiを変数として、src1、src2それぞれの要素の加算を配列ansに格納する動作をインラインアセンブラで作成しています。 そして、高速化を目指しMMXを使用しているのですが、うまく動作しません。 動作結果としては配列ansに正しい解が代入されず、全ての要素が 205? になっています。 どこかに問題があるのですが、未熟な自分では間違いに気づくことができません。 ご指摘お願いいたします。  #include <stdio.h> #include <stdlib.h> #include <string.h> #define LEN 64 int main(void){ unsigned char *src1, *src2, *ans; unsigned char *t1, *t2, *t3; int i, j; src1 = (unsigned char *)malloc(sizeof(unsigned char) * LEN); src2 = (unsigned char *)malloc(sizeof(unsigned char) * LEN); ans = (unsigned char *)malloc(sizeof(unsigned char) * LEN); t1 = src1; t2 = src2; t3 = ans; i = LEN / 8; for(j=0; j<LEN; j++){ //配列src1、src2の初期化 src1[j] = j; src2[j] = j + 1; } _asm { mov ecx, i LOOPSTART: movq mm0, src1 movq mm1, src2 paddusb mm0, mm1 movq ans, mm0 add src1, 8 add src2, 8 add ans, 8 loop LOOPSTART emms } src1 = t1; src2 = t2; ans = t3; for(i=0; i<LEN; i++) printf("%d + %d = %d\n", src1[i], src2[i], ans[i]); return 0; }

  • アルファベットを入力するとすべてのアルファベットを小文字に変換して出力

    アルファベットを入力するとすべてのアルファベットを小文字に変換して出力するプログラムを作成しました。2度以上同じ処理を行う場合はその部分を関数としなければいけないのですが、実行結果のように出力できませんでした。。どこが間違っているのでしょうか? 実行結果↓ ************ AOmori ==>aomori iwate ==>iwate AKITA ==>akita ************ #include <stdio.h> #include <string.h> #include <ctype.h> int henkan(void); int main(void) { char str[100]; int j,len; for(j=0;j<6;j++){ gets(str); printf("==>"); } henkan(); return 0; } int henkan(void) { char str[100]; int i, chk,len; for(i=0;i<len;i++){ chk=isupper(str[i]); if(chk!=0)str[i]=tolower(str[i]); { printf("%c",str[i]); } } printf("\n"); return chk; }