Entries

俺の書いたコードがこんなに遅いわけがない(HSP編)

○概要
 よく言われることですが、HSPは遅いです。
 より正確に言えば、普通の構文(四則演算・条件判断・反復処理)が遅いです。
 HSPから、C言語など他の言語にステップアップした人(はい、私です)ならよーく身にしみる話でしょう。
 ……まあ、何を言ってもスクリプト言語ですしおすしで済んでしまいそうなのですが、いちどはわるあがきしてみるのもいいべんきょうになるです?

※以下、Core-i5 3210M/メモリ4GBなMyノーパソでの測定になります。


○まずは測定用の足場をば
 HSPで計算時間を計りたい場合は、Win32APIを叩くのが近道でしょう。
 つまりはこういうことです。

#uselib "winmm.dll"
#cfunc timer "timeGetTime"
#func timeBeginPeriod "timeBeginPeriod" int
#func timeEndPeriod "timeEndPeriod" int
timeBeginPeriod 1
time_s=timer()
/* この間に計測したいコードを差し込む */
mes ""+(timer()-time_s)+"ms"
timeEndPeriod 1



○最初にループ呼び出しのコストを測定してみる
 手始めに、「repeat 100000000 :loop」というコードを間に挟んで10回測定してみたところ、
平均で1974ミリ秒(平均値の平均二乗誤差は4.290ミリ秒)となった。
 一方、「for k,0,10000000 :next」というコードを間に挟んで10回測定してみたところ、
平均で1202ミリ秒(平均値の平均二乗誤差は1.898ミリ秒)となった。
 じゃあfor~nextの方が速いかといえば全然そうではなく、1ループ辺りではrepeat:for=1.974e-5[ms]:1.202e-4[ms]=6:1ぐらいです。


○次に四則演算・論理演算のコストでも測定してみる
 「+」(repeat 20000000:a=123+456:loop)→8.691x10^-5[ms/loop]
 「-」(repeat 20000000:a=123-456:loop)→8.664x10^-5[ms/loop]
 「*」(repeat 20000000:a=123*456:loop)→8.600x10^-5[ms/loop]
 「/」(repeat 20000000:a=123/456:loop)→8.764x10^-5[ms/loop]
 「\」(repeat 20000000:a=123%456:loop)→8.769x10^-5[ms/loop]
 「&」(repeat 20000000:a=123&456:loop)→8.840x10^-5[ms/loop]
 「|」(repeat 20000000:a=123|456:loop)→8.664x10^-5[ms/loop]
 「^」(repeat 20000000:a=123^456:loop)→8.636x10^-5[ms/loop]
 「<<」(repeat 20000000:a=123<<4:loop)→8.705x10^-5[ms/loop]
 「>>」(repeat 20000000:a=123>>4:loop)→8.738x10^-5[ms/loop]
 「++」(repeat 30000000:a++:loop)→4.707x10^-5[ms/loop]
 「--」(repeat 30000000:a--:loop)→4.730x10^-5[ms/loop]

……インクリメント・デクリメント以外全然変わらないことが分かった。というかインクリメントって足し算の1.8倍速いんだね。
(ちなみに実数でもやってみたが、「全体的に3割ほど遅くなる」ことぐらいで掲載する価値無さそうなので略)


○同じ内容のコードを様々な方法で処理してみる

#uselib "winmm.dll"
#cfunc timer "timeGetTime"
#func timeBeginPeriod "timeBeginPeriod" int
#func timeEndPeriod "timeEndPeriod" int
#define size 300
timeBeginPeriod 1
dim a,size,size :dim b,size,size :dim c,size,size
time_s=timer()
repeat size
  i=cnt
  repeat size
    j=cnt
    repeat size
      c.i.j+=a.i.cnt*b.cnt.j
    loop
  loop
loop
mes ""+(timer()-time_s)+"ms"
timeEndPeriod 1
stop


 これはいわゆる「行列積」と呼ばれるものである。このまま実行したところ、10回平均で8834[ms]掛かった。
 「#runtime "hsp3mt"」を使えばちょっとは速くなるかと思ったが、結果は平均8534[ms]でさほどの変化なし。
 本体同梱のソースコンバータでC++のコードに変換して実行すると、平均で4484[ms]。倍速やっほい……と思っていたら甘かった。
 本体同梱版のHSPLetを使用すると(API直叩きをgettime関数に書き換える必要あり)、平均計算時間はなんと398[ms]。演算速度22倍とかやりすぎだろJK……。

 ちなみに、普通にC言語で書いてVC++2010で「/O2 /arch:SSE2」としてコンパイルしたら平均24[ms]だった。以下、ソース。

#include <stdio.h>
//Win32API用
#include <windows.h>
#include <mmsystem.h>
#pragma comment (lib, "winmm.lib")
#define size 300

int a[size][size], b[size][size], c[size][size];

int main(void){
  int i, j, k, n;
  unsigned int starttime;

  for(i = 0; i < size; i++){
    for(j = 0; j < size; j++)
      a[i][j] = b[i][j] = c[i][j] = 0;
  }
  printf("初期化完了\n");
  timeBeginPeriod(1);
  starttime = timeGetTime();
  for(n = 0; n < 1000; n++){
    for(i = 0; i < size; i++){
      for(j = 0; j < size; j++){
        for(k = 0; k < size; k++){
          c[i][j] += a[i][k] * b[k][j];
        }
      }
    }
  }
  printf("%d\n", timeGetTime() - starttime);
  timeEndPeriod(1);

  return 0;
}



○参考(になりそうな)ページ
 「飽くなき高速化への道(演算編)」
  http://hp.vector.co.jp/authors/VA015883/hsp/hna/hnafast1.html
 「HSP高速化Tips」
  http://hp.vector.co.jp/authors/VA022217/tips/hsp/bench.html
  http://meteoricstream.com/tips/hsp/bench.html
 「HSPで高速並列演算」
  http://blog.livedoor.jp/toropippi/archives/91194.html
 「HSPで分解能1ms以下の時間計測ができるか」
  http://ofmind.net/doc/experiments-by-hsp3
 「高速な文字列の結合(連結)」
  http://rpen.blogspot.jp/2007/12/blog-post_16.html
 「番外企画・速度にまつわる小ネタ達の巻」
   http://www.hspdx.net/hspyarou/2001.html
 「HSPでの最適化 [ Programming Lab ]」
  http://sky.geocities.jp/krypton_pl/hsp/hspopt.htm
 「#cmpopt optcode 1って効いてるのか?」
  http://hsp.tv/play/pforum.php?mode=pastwch&num=51854


 以上。
関連記事
スポンサーサイト
この記事にトラックバックする(FC2ブログユーザー)
http://ysrken.blog.fc2.com/tb.php/47-40d9bf92

トラックバック

コメント

コメントの投稿

コメントの投稿
管理者にだけ表示を許可する

Appendix

プロフィール

YSR

Author:YSR
「YSR」「YSRKEN」「◆YSRKENkO6Y(~2013/08/25)」「◆YSRKEN.ceVZZ(2013/08/26~)」として活動しています。
プログラミングと艦これが趣味です。
プロフ画像はCrystalDiskInfoの水晶雫ちゃんです。
主な創作物についてはhttp://ysrken.blog.fc2.com/blog-entry-76.htmlをご覧ください。

カレンダー(月別)

08 ≪│2017/09│≫ 10
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

全記事表示リンク

全ての記事を表示する

QRコード

QR

総アクセス数

アクセス数

現在の閲覧者数: