強火で進め

このブログではプログラム関連の記事を中心に書いてます。

(修正)float型とdouble型を比較した場合、常にfloatが速いと思ってはダメらしい

すみません、以前のプログラムにポカがありました

以前、以下のエントリーを書きました。

float型とdouble型を比較した場合、常にfloatが速いと思ってはダメらしい - 強火で進め
http://d.hatena.ne.jp/nakamura001/20090226/1235641233

そして今回、この速度差がどこから来てるのかアセンブリレベルで検証しようと色々と前回のプログラムを見直していたところ大ポカをやらかしている事に気がつきました。

具体的には以下の様な部分です。

NSLog(@"double                           : time=%f\tval=%f",  elapsedTime, floatTotal);

こちら double での計算結果なので本来でればここで指定するのは floatTotal ではなく doubleTotal です。

このようなポカをやらかしたせいで doubleTotal が実際に使用される部分が無いために

コンパイラによる最適化や削除が入らない様なプログラムとなる様に気をつける

という指標を掲げていたにもかかわらずコンパイラによる削除が入ってしまい、それが前回の様な10倍もの速度差に繋がった様です。

前回のエントリーを見てプログラムの修正などされた方がおられましたらご迷惑をおかけしましてすみません。

修正した検証プログラム

修正した検証プログラムは以下となります。前回と同様にボタンなどのActionとして設定し、呼び出して下さい。

#define TEST_NUM    300
#define LOOP_NUM    100000

float floatVal[LOOP_NUM];
double doubleVal[LOOP_NUM];

- (IBAction)run:(id)sender
{    
    int i, j;
    double startTime;
    double elapsedTime;
    double doubleTmp;
    float floatTotal;
    float floatTmp;
    double doubleTotal;
    
    srand((unsigned)CFAbsoluteTimeGetCurrent());
    
    for (i=0; i<LOOP_NUM; i++) {
        doubleVal[i] = (double)(rand() % 100) / 100.0;
        floatVal[i] = (float)doubleVal[i];
    }
    
    // float型の場合の速度を計測
    startTime = CFAbsoluteTimeGetCurrent();
    floatTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            floatTotal += floatVal[j] - 0.8f*i/TEST_NUM;
        }
        floatTotal *= 0.1f;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"float                            : time=%f\tval=%f",  elapsedTime, floatTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    floatTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            floatTotal += floatVal[j] - 0.8f*(float)i/TEST_NUM;
        }
        floatTotal *= 0.1f;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"float(変数をキャスト)              : time=%f\tval=%f",  elapsedTime, floatTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    floatTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            floatTotal += floatVal[j] - 0.8f*i/(float)TEST_NUM;
        }
        floatTotal *= 0.1f;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"float(定数をキャスト)              : time=%f\tval=%f",  elapsedTime, floatTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    floatTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            floatTotal += floatVal[j] - 0.8f*(float)i/(float)TEST_NUM;
        }
        floatTotal *= 0.1f;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"float(両方をキャスト)              : time=%f\tval=%f",  elapsedTime, floatTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    floatTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            doubleTmp = floatVal[j] - 0.8*i/TEST_NUM;
            floatTotal += (float)doubleTmp;
        }
        floatTotal *= 0.1f;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"float(異なる型のデータを使ったとき)  : time=%f\tval=%f",  elapsedTime, floatTotal);
    
    // double型の場合の速度を計測
    startTime = CFAbsoluteTimeGetCurrent();
    doubleTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            doubleTotal += doubleVal[j] - 0.8*i/TEST_NUM;
        }
        doubleTotal *= 0.1;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"double                           : time=%f\tval=%f",  elapsedTime, doubleTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    doubleTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            doubleTotal += doubleVal[j] - 0.8*(double)i/TEST_NUM;
        }
        doubleTotal *= 0.1;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"double(変数をキャスト)             : time=%f\tval=%f",  elapsedTime, doubleTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    doubleTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            doubleTotal += doubleVal[j] - 0.8*i/(double)TEST_NUM;
        }
        doubleTotal *= 0.1;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"double(定数をキャスト)             : time=%f\tval=%f",  elapsedTime, doubleTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    doubleTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            doubleTotal += doubleVal[j] - 0.8*(double)i/(double)TEST_NUM;
        }
        doubleTotal *= 0.1;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"double(両方をキャスト)             : time=%f\tval=%f",  elapsedTime, doubleTotal);
    
    startTime = CFAbsoluteTimeGetCurrent();
    doubleTotal = 0.0f;
    for (i=0; i<TEST_NUM; i++) {
        for (j=0; j<LOOP_NUM; j++) {
            floatTmp = doubleVal[j] - 0.8f*i/TEST_NUM;
            doubleTotal += floatTmp;
        }
        doubleTotal *= 0.1;
    }
    elapsedTime =  CFAbsoluteTimeGetCurrent() - startTime;
    NSLog(@"double(異なる型のデータを使ったとき) : time=%f\tval=%f",  elapsedTime, doubleTotal);
}

速度比較

速度比較した結果は以下の様になりました。 float の方が速くなりましたorz

Compile for Thumbにチェック有り(Thumb)
処理時間 計算結果
float 6.067997 -3349.595703
float(変数をキャスト) 6.026205 -3349.595703
float(定数をキャスト) 6.005627 -3349.595703
float(両方をキャスト) 6.079855 -3349.595703
float(異なる型のデータを使ったとき) 11.498240 -3349.595703
double 6.399619 -3349.497078
double(変数をキャスト) 6.409551 -3349.497078
double(定数をキャスト) 6.396411 -3349.497078
double(両方をキャスト) 6.393770 -3349.497078
double(異なる型のデータを使ったとき) 11.677976 -3349.496793
Compile for Thumbにチェック無し(ARM)
処理時間 計算結果
float 1.656111 -3349.364990
float(変数をキャスト) 1.734671 -3349.364990
float(定数をキャスト) 1.690053 -3349.364990
float(両方をキャスト) 1.689119 -3349.364990
float(異なる型のデータを使ったとき) 2.878375 -3349.364990
double 3.282322 -3349.269300
double(変数をキャスト) 3.284180 -3349.269300
double(定数をキャスト) 3.732849 -3349.269300
double(両方をキャスト) 3.303846 -3349.269300
double(異なる型のデータを使ったとき) 4.520657 -3349.269015

結果の検証

  • float と double の速度比較はiPhoneでは float に軍配が上がる
  • 「Compile for Thumbにチェック無し(ARM)」の方が速い

まとめ

float型とdouble型を比較した場合、常にfloatが速いと思ってはダメらしいがiPhoneについては float を使った方が速い。