Hatena::Diary

hogeなlog

プロフィール

hogelog

hogelog

小室 直(こむろ すなお)。電気通信大学2003年入学。2009年現在修士1。

カレンダー
1984 | 01 |
2006 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 |

November 05(Thu), 2009

[][][] stackallocもうちょい整理

githubに。なんかライブラリ風にまとめてみた。stackallocのテストと同様の動作をするobstackを使うプログラムも同梱。

http://github.com/hogelog/stackalloc

総じてobstackの方が性能良いけど、stackallocは使う道具を標準libcに限ってるので、こんなもんかなと思いたい。

[] わいは鬼や! 就活の鬼や!

TAKESAKO @ Yet another Cybozu Labs: 就活生向けIT業界セミナーで講演します

IT業界の最先端で活躍しているエンジニアは何を考えどう生きているのか!?

IT業界でエンジニアとして働きたいと思っている人には是非参加してほしいセミナーです。

昨年までは、サイボウズ・ラボの畑、奥、蓑輪、天野が、各地で講演しましたが、今年はサイボウズ・ラボの竹迫が全国を飛び回ります。今回の講演のためにニンテンドーDSで新しいソフトを買って修行して参りたいと思います。

たぶん初めての就職活動ですと、いつから何をし始めればよいのかよくわからないと思います。言われるがままに自己分析をしてみたり、将来のライフスタイルを設計してみたり、企業分析をしてエントリーシートをひたすら書いて応募してみたりと、あわただしく動き始めていると思います。

とりあえず、ここではそんな堅苦しいことは一切なしに、気軽に参加していただければと思います。

参加無料、スーツ禁止です!

※ 万が一リクルートスーツでご来場いただいた場合は入場をお断りする場合がございます。

座談会は希望者のみの自由参加ですが、(会社からは怒られるかもしれませんが)本音でみなさんと楽しくお話しできればと思います。

よろしくお願いします!

申し込みました。

via http://www.atdot.net/~ko1/diary/200911.html#d3

November 04(Wed), 2009

[][][] 切り出してみる。

要はこういうスタック割り当て、案外便利っすよと。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct Frame {
  struct Frame *prevframe;
  void *top;
  size_t index;
} Frame;
typedef struct Slot {
  void *slot;
  void *last;
  size_t size;
} Slot;
typedef struct Stack {
  Slot *slots;
  size_t slotsnum;
  Frame *lastframe;
  void *top;
  size_t index;
} Stack;

#define STACK_MINSLOTSIZE 1024
static Slot *addslot(Stack *s, size_t slotsize) {
  size_t n = s->slotsnum;
  s->slots = realloc(s->slots, (n+1)*sizeof(Slot));
  s->slots[n].slot = malloc(slotsize);
  s->slots[n].last = (void*)((char*)s->slots[n].slot + slotsize);
  s->slotsnum = n+1;
  return s->slots[n].slot;
}
static size_t stack_grow(Stack *s) {
  size_t nextindex = s->index + 1;
  size_t slotsnum = s->slotsnum;
  if (nextindex >= slotsnum) {
    int i;
    size_t newsize = 0;
    Slot *newslot;
    for(i=0;i<slotsnum;++i) {
      Slot *slot = &s->slots[i];
      newsize += (char*)slot->last - (char*) slot->slot;
    }
    addslot(s, newsize);
  }
  s->index = nextindex;
  s->top = s->slots[nextindex].slot;
  return s->index;
}
void *stack_alloc(Stack *s, size_t size) {
  void *new = s->top;
  void *newtop = (void*)((char*)new + size);
  Slot *curslot = &s->slots[s->index];
  if (newtop > curslot->last) {
    stack_grow(s);
    return stack_alloc(s, size);
  }
  s->top = newtop;
  return new;
}
Frame *stack_newframe(Stack *s) {
  void *ptop = s->top;
  size_t pindex = s->index;
  Frame *f = stack_alloc(s, sizeof(Frame));
  if (f) {
    f->prevframe = s->lastframe;
    f->top = ptop;
    f->index = pindex;
    s->lastframe = f;
  }
  return f;
}
Frame *stack_closeframe(Stack *s) {
  Frame *f = s->lastframe;
  s->top = f->top;
  s->index = f->index;
  s->lastframe = f->prevframe;
  return f;
}
Stack *stack_init(Stack *s) {
  s->slots = NULL;
  s->slotsnum = 0;
  s->lastframe = NULL;
  addslot(s, STACK_MINSLOTSIZE);
  s->index = 0;
  s->lastframe = NULL;
  s->top = s->slots[0].slot;
  s->index = 0;
  return s;
}
void stack_close(Stack *s) {
  int i;
  for(i=s->slotsnum-1;i>=0;--i) {
    free(s->slots[i].slot);
  }
  free(s->slots);
  s->slots = NULL;
}

/** stackalloc test code **/
static int *new_int(Stack *s, const int i) {
  int *dst = stack_alloc(s, sizeof(int));
  *dst = i;
  return dst;
}
typedef struct Array {
  size_t size;
  size_t length;
  char buffer[1];
} Array;
#define array_elem(a, i) ((void*)&((a)->buffer[(a)->size*(i)]))
static Array *new_array(Stack *s, size_t size, size_t len) {
  Array *a = stack_alloc(s, sizeof(Array)+size*(len-1));
  a->size = size;
  a->length = len;
  return a;
}
static Array *new_range(Stack *s, int start, int end) {
  size_t len = end - start + 1;
  Array *a = new_array(s, sizeof(int), len);
  int i;
  for(i=0;i<len;++i) {
    int *p = array_elem(a, i);
    *p = i+start;
  }
  return a;
}
void test01() {
  Stack s_;
  Stack *s = &s_;
  int i, j;
  void *p;
  stack_init(s);
  for(i=0;stack_newframe(s) && i<2;++i) {
    int *sum = new_int(s, 1);
    Array *a = new_range(s, 1, 10);
    for(j=0;stack_newframe(s) && j<10;++j) {
      int *p = array_elem(a, j);
      *sum *= *p;
      printf("%2d(%p) = %7d(%p)\n", *p, p, *sum, sum);
      stack_closeframe(s);
    }
    stack_closeframe(s);
  }
  stack_close(s);
}
int main(int argc, char **argv) {
  test01();
  return 0;
}

がこういう実行結果。

% ./stackalloc
 1(0x99f7030) =       1(0x99f7024)
 2(0x99f7034) =       2(0x99f7024)
 3(0x99f7038) =       6(0x99f7024)
 4(0x99f703c) =      24(0x99f7024)
 5(0x99f7040) =     120(0x99f7024)
 6(0x99f7044) =     720(0x99f7024)
 7(0x99f7048) =    5040(0x99f7024)
 8(0x99f704c) =   40320(0x99f7024)
 9(0x99f7050) =  362880(0x99f7024)
10(0x99f7054) = 3628800(0x99f7024)
 1(0x99f7030) =       1(0x99f7024)
 2(0x99f7034) =       2(0x99f7024)
 3(0x99f7038) =       6(0x99f7024)
 4(0x99f703c) =      24(0x99f7024)
 5(0x99f7040) =     120(0x99f7024)
 6(0x99f7044) =     720(0x99f7024)
 7(0x99f7048) =    5040(0x99f7024)
 8(0x99f704c) =   40320(0x99f7024)
 9(0x99f7050) =  362880(0x99f7024)
10(0x99f7054) = 3628800(0x99f7024)

使いどころは結構あるんじゃないかなー。論文で読んでなるほどなー思って適当に書いてるんだけど、どうなんだろう。メジャーなこういう用途のライブラリ無いのか。


スタック領域そのものをreallocせずにslotとかいう気持ち悪い単位で区切って管理してるのは、アドレス変更されたら嫌だからなんですけど、なんかもうちょいやりようあるような。1024バイトの空のslot一つだけあるときに4096バイトの領域要求したら、1024, 1024, 2048, 4096バイトという4本のslotができて、3つのslotの4096バイトになる領域が無駄になるとか。

あとWORD境界に揃えた方がいいかなーとか。

malloc捨てたりmadviceとかで性能向上とかもすんだろか。やっぱそういうライブラリ無いのか気になる。

[][][] stackalloc的なものあった

obstackという、正にそのものがあった。MIT LicenseのLuaに組み込む目的なのでインターフェースなど参考にしつつ、こっちを組み込んでビルドすることもできるようにしておこう。

thanks! alohakun id:kikx

November 02(Mon), 2009

[][] 就活中です!

電気通信大学 電気通信学研究科 情報工学専攻 阿部研究室 博士課程前期 一年 小室 直(こむろ すなお)ですよろしくお願いします!

October 24(Sat), 2009

[][][] Lua高速化プロジェクト開発状況など(3)

ひどい話でObjectStackのリサイズをまだ実装していない。スタックそのものをreallocしたあとには当然スタック割り当てを指してるオブジェクト達のポインタを修正してやらにゃならんのだけど、どっかで漏れてるようでうまくいかない。気分転換にプロファイリングなど。

使う道具はOProfileで。あとOProfileでテストするときのために適当に書いたスクリプトoptest.rbを使います。

先の日記 Lua高速化プロジェクト開発状況など(1) - hogeなlog に書きましたがなんだか結構な高速化に成功しました。ただ「速くなってる! よくわかんないけど!」では「なんで? 正しい結果じゃないんじゃないの?」と言われたときに困ります。「こうこうこんな理由で速くなってるのです! 謝罪しろ!」と言い返すためにちゃんと理由を調べましょう。

OProfileについては以前ちょっとだけ解説を試みた記事があるので、読むと何かわかったような気分になれるかもしれません。

時間のプロファイル

まずは単純に「元の実装はどこで時間くってるのか」調べるために、1クロックごとに発生する(らしい)CPU_CLK_UNHALTEDをサンプリング。

% sudo opcontrol --event=CPU_CLK_UNHALTED:10000
% optest ./lua-original ao.lua --report=optest-original
# /usr/bin/sudo /usr/bin/opcontrol --image=./lua-original
# /usr/bin/sudo /usr/bin/opcontrol --shutdown
Stopping profiling.
Killing daemon.
# /usr/bin/sudo /usr/bin/opcontrol --reset
# /usr/bin/sudo /usr/bin/opcontrol --start
Using 2.6+ OProfile kernel interface.
Using log file /var/lib/oprofile/samples/oprofiled.log
Daemon started.
Profiler running.
# ./lua-original ao.lua
--- aobench ---
init scene
render start
save ppm
time to render end      214
total time      214
--- done ---
# /usr/bin/sudo /usr/bin/opcontrol --dump
# /usr/bin/sudo /usr/bin/opcontrol --stop
Stopping profiling.
# /usr/bin/opreport ./lua-original
# /usr/bin/opreport -l ./lua-original
# /usr/bin/opannotate -as ./lua-original
# /usr/bin/opannotate -a ./lua-original
# /usr/bin/opannotate -s ./lua-original
% cat optest-original.report.l
CPU: AMD64 processors, speed 2410.97 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Cycles outside of halt state) with a unit mask of 0x00 (No unit mask) count 10000
samples  %        symbol name
5192807  38.9721  luaV_execute
1943239  14.5841  luaV_gettable
1555359  11.6730  luaH_get
833782    6.2576  luaD_precall
550677    4.1328  setnodevector
535552    4.0193  propagatemark
344504    2.5855  luaM_realloc_
326751    2.4523  luaD_poscall
267898    2.0106  luaV_settable
267091    2.0045  newkey
256199    1.9228  luaH_free
229004    1.7187  luaC_link
150810    1.1318  l_alloc
139415    1.0463  luaH_set
112902    0.8473  mainposition
80198     0.6019  luaH_getnum
76348     0.5730  luaH_new
61315     0.4602  sweeplist
49088     0.3684  setarrayvector
36823     0.2764  math_random
32033     0.2404  math_sqrt
30470     0.2287  luaV_lessthan
27994     0.2101  luaO_fb2int
27718     0.2080  lua_pushnumber
25408     0.1907  luaL_checknumber
21021     0.1578  singlestep
20194     0.1516  hashnum
18066     0.1356  math_sin
17196     0.1291  math_cos
15462     0.1160  index2adr
14060     0.1055  math_abs
13663     0.1025  luaO_log2
12803     0.0961  reallymarkobject
12411     0.0931  lua_tonumber
...
# ./lua-stackalloc-dev ao.lua
--- aobench ---
init scene
render start
save ppm
time to render end      152
total time      152
--- done ---
...
% cat optest-stackalloc-dev.report.l
CPU: AMD64 processors, speed 2410.97 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Cycles outside of halt state) with a unit mask of 0x00 (No unit mas
k) count 10000
samples  %        image name               symbol name
5661788  46.8418  lua-stackalloc-dev       luaV_execute
2155330  17.8317  lua-stackalloc-dev       luaV_gettable
1587087  13.1305  lua-stackalloc-dev       luaH_get
910107    7.5296  lua-stackalloc-dev       luaD_precall
340603    2.8179  lua-stackalloc-dev       luaD_poscall
279875    2.3155  lua-stackalloc-dev       luaV_settable
265766    2.1988  lua-stackalloc-dev       newkey
213770    1.7686  lua-stackalloc-dev       luaH_ostack_new
149016    1.2329  lua-stackalloc-dev       luaH_set
114820    0.9499  lua-stackalloc-dev       mainposition
91977     0.7610  lua-stackalloc-dev       luaH_getnum
29226     0.2418  lua-stackalloc-dev       ostack_alloc_
28518     0.2359  lua-stackalloc-dev       luaO_fb2int
27360     0.2264  lua-stackalloc-dev       math_sqrt
27211     0.2251  lua-stackalloc-dev       luaV_lessthan
27113     0.2243  lua-stackalloc-dev       hashnum
25205     0.2085  lua-stackalloc-dev       luaL_checknumber
24763     0.2049  lua-stackalloc-dev       math_random
24707     0.2044  lua-stackalloc-dev       lua_pushnumber
19452     0.1609  lua-stackalloc-dev       luaO_log2
18235     0.1509  lua-stackalloc-dev       math_sin
17151     0.1419  lua-stackalloc-dev       math_cos
14647     0.1212  lua-stackalloc-dev       index2adr
12645     0.1046  lua-stackalloc-dev       lua_tonumber
10246     0.0848  lua-stackalloc-dev       math_abs
...

luaV_execute, luaH_gettableに時間かかってるのはどちらも同じだけど、元実装だとGCとreallocに時間がかかっていることが読み取れますね、と。stackalloc-devではこの二つはではスタック割り当てにより、ほとんど呼ばれていない。そりゃ速くなります。

次はキャッシュミスを。L2キャッシュまでにヒットしてりゃあ十分速い処理になってんだろということで、L2_CACHE_MISSでもサンプリングしてみる。

% sudo opcontrol --event=L2_CACHE_MISS:500
% optest ./lua-original ao.lua --report=optest-original
# /usr/bin/sudo /usr/bin/opcontrol --image=./lua-original
# /usr/bin/sudo /usr/bin/opcontrol --shutdown
Stopping profiling.
Killing daemon.
# /usr/bin/sudo /usr/bin/opcontrol --reset
# /usr/bin/sudo /usr/bin/opcontrol --start
Using 2.6+ OProfile kernel interface.
Using log file /var/lib/oprofile/samples/oprofiled.log
Daemon started.
Profiler running.
# ./lua-original ao.lua
--- aobench ---
init scene
render start
save ppm
time to render end      187
total time      187
--- done ---
...
% optest ./lua-stackalloc-dev ao.lua --report=optest-stackalloc-dev
# /usr/bin/sudo /usr/bin/opcontrol --image=./lua-stackalloc-dev
# /usr/bin/sudo /usr/bin/opcontrol --shutdown
Stopping profiling.
Killing daemon.
# /usr/bin/sudo /usr/bin/opcontrol --reset
# /usr/bin/sudo /usr/bin/opcontrol --start
Using 2.6+ OProfile kernel interface.
Using log file /var/lib/oprofile/samples/oprofiled.log
Daemon started.
Profiler running.
# ./lua-stackalloc-dev ao.lua
--- aobench ---
init scene
render start
save ppm
time to render end      136
total time      136
--- done ---
...
% cat optest-original.report.l
CPU: AMD64 processors, speed 2410.97 MHz (estimated)
Counted L2_CACHE_MISS events (L2 Cache Misses) with a unit mask of 0x07 (multiple flags) count 500
samples  %        symbol name
197869   38.6208  propagatemark
111403   21.7440  setnodevector
65256    12.7369  luaM_realloc_
42544     8.3039  luaH_free
32065     6.2586  luaV_execute
23609     4.6081  luaC_link
12976     2.5327  l_alloc
11490     2.2427  luaH_new
3153      0.6154  reallymarkobject
3044      0.5941  sweeplist
2211      0.4316  singlestep
1525      0.2977  luaV_gettable
1048      0.2046  luaH_set
1045      0.2040  luaC_step
938       0.1831  luaH_get
682       0.1331  luaD_precall
145       0.0283  lua_gettop
134       0.0262  setarrayvector
133       0.0260  luaC_separateudata
123       0.0240  markroot
...
% cat optest-stackalloc-dev.report.l
CPU: AMD64 processors, speed 2410.97 MHz (estimated)
Counted L2_CACHE_MISS events (L2 Cache Misses) with a unit mask of 0x07 (multiple flags) count 500
samples  %        symbol name
212      22.3629  setarrayvector
197      20.7806  resize
152      16.0338  luaM_realloc_
129      13.6076  luaD_precall
102      10.7595  setnodevector
101      10.6540  luaC_link
14        1.4768  newkey
10        1.0549  luaH_free
8         0.8439  luaV_settable
6         0.6329  luaH_new
5         0.5274  luaS_newlstr
5         0.5274  luaV_execute
2         0.2110  luaH_set
1         0.1055  addk
1         0.1055  luaS_resize
1         0.1055  luaV_gettable
1         0.1055  luaX_init
1         0.1055  math_random

こちらの値は驚くほどの違いが。元の実装のキャッシュミスの数がすさまじい理由の根源はGCですね。まずLuaのGCはIncremental MarkSweep GC。MarkSweepの「Markフェーズでヒープ領域全部触るからキャッシュミスしまくる」という弱点がモロに。GCでキャッシュを荒されたおかげで、他の部分でもキャッシュミス発生しまくり。それらをほとんど解消しているようで、これは喜ばしいことです。

キャッシュヒット率が非常に上がったのであれですね。「省電力です!」と強弁することすら可能で、これは喜ばしいことです。エコ! エコ!

October 23(Fri), 2009

[][] Lua高速化プロジェクト開発状況など(2)

refixするように実装した。

% cat |./src/lua-stackalloc-dev
local t = {10, 20}
local u = {t}
u[1][1] = u[1][1] + 100
print("t[1]", t[1])
print("u[1][1]", u[1][1])
v = {t}
v[1][1] = v[1][1] + 100
print("t[1]", t[1])
print("u[1][1]", u[1][1])
print("v[1][1]", v[1][1])

t[1]    110
u[1][1] 110
t[1]    210
u[1][1] 210
v[1][1] 210

した。なんの疑問も無くrefixと名付けたけどrefixってなんだ……

懸念だった速度のコスト

% ./lua-gctime-stackalloc-dev ao.lua
--- aobench ---
init scene
render start
save ppm
time to render end      138
total time      138
--- done ---
## execution: 138.683659 s, gc: 0.000093 s

とりあえずAObench程度のプログラムなら問題無いようだ。

Windows環境

Windows環境でも実験できるようにしとこう。手元のノートPCである程度試したりはしてるけども。

最近のコメント