HyperCard tribute

スピードアップさせるために


 ハイパーカードはとても柔軟な言語であり、複雑な動作を非常に簡単にできるようになっています。しかし、この大変便利な特徴のおかげでスピードが遅くなってしまっているのも確かです。
 スピード低下の原因にはこの他にも、HyperCardがハードディスクへのアクセスを多用していることも大きな原因となっています。例えば、HyperCardには「保存」メニューがない代わりに様々なタイミングで自動保存(ハードディスクへの書き込み)を行っています。他にも、データをメモリに読み込ずにハードディスクからデータを取る場合なども多くあるのです。
 これらが、HyperCardが遅くなる原因なのですが、もう一つの原因に速く動くようなスクリプトの組み方をしていないという事も挙げられるのです。HyperCardの性質を理解し、速く動かす為の工夫をすれば10%はスピードを高めることが可能です。
 ここでは、スピードを速くするための方法と、遅さを感じさせないスクリプト作りについても紹介します。

  • 反応を早くする
  • 画面をロックする
  • フィールドの代わりに変数を使う
  • フィールドはhideして使う
  • カードを移動するのでなく、参照する
  • 変数名を短くする
  • 文字列はダブルクォーテーションでくくる
  • イコール(等しい)でなく、ノットイコール(等しくない)を使う
  • 関数は the xxxx を使う
  • 複数の式をまとめる
  • 変数の参照を少なくする
  • ループ構造の中は短くする
  • ハンドラからハンドラを呼び出さない
  • lineマcharマitemマwordの順に利用する
  • 日本語を表示しないフィールドは英数字フォントを使う



    ■反応を早くする
     人間が遅く感じる理由は、大きく分けて2つあります。1つ目は実際のスピード。2つ目は反応速度です。例えば、ボタンをクリックした時に反応がすぐに返ってこない、フィールドのスクロールの速度が遅い、待ち時間の間に何も反応がないなどです。
     この理由には2つの原因があります。

    1.スタック内のオブジェクトとデータの量が多い
    2.反応を返す工夫をしていない

     カードの枚数は、多くなればなるほど反応が鈍くなります。カードの枚数が多いという理由とそのカードに含まれるオブジェクト、データが多くなるからです。このために、かなり多くのデータを必要とする場合は、スタックからスタックを呼び出すなどして分割する方法をとるなどすれば良いでしょう。
     ボタンの数は、特に大きな影響を与えます。ボタンをなるべく作らないようにするという事と、メニューやパレットを用いるなどして、できる限り少なくするようにしましょう。
     フィールドの数は上の2つに比べると、それほど遅くなるという事はありません。それよりもフィールドの場合は、1つのフィールドに書き込まれているデータの量と、画面に実際に表示されているデータの量による原因の方が速度低下が大きいのです。必要のないデータであれば、できるだけ hide しておきましょう。これにより、フィールドに多量のテキストが入っていたとしても速度低下はほとんど起こらなくなります。

     反応を返す工夫をしてみます。例えば、とても簡単な事ですが、ボタンのハイライトをオンにする、クリック音を鳴らす、待ち時間にカーソルの形態をMacお馴染みの腕時計カーソルやビーチボールに変える、待ち時間に合間を縫ってユーザーに視覚的なイベントを見せるなどです。
     ボタンのハイライトをオンにすれば、クリックした瞬間にボタンが反転しますので、クリックしたことがユーザーに直に伝えることができるようになります。
     クリック音を鳴らすようにすれば、その音が鳴っているほんの少しの間だけでも待っているという感覚から抜け出すことができるようになります。
     カーソルの形態を変えれば、長い待ち時間も「ちゃんと仕事をしてるよ」という事を感じとることができます。人を待っている時に何も連絡がないよりは、時々電話連絡があれば、待ち時間はかなり短く感じることと思います。これと同じ事ではないでしょうか。
     待ち時間の合間に、例えばメッセージボックスに、現在の状況をユーザーに伝えるなどをしてみてはどうでしょうか。単純なところでは、残り何秒などと表示するなどです。これにより、スピードは逆に遅くなるかもしれません。しかし、感覚的には速く感じるのではないでしょうか。



    ■画面をロックする
     ユーザーに見せる必要のない処理の場合は、lockscreenをして画面の書き換えをロックします。例えば、画面上のフィールド全てを表示したり、データを代入したり、カードを巡回してデータを拾ってくるようなスクリプトの時は大きな効果が現れます。

    「画面をロックする場合」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put the ticks into s
      lock screen
      put 0 into cd fld "test"
      repeat with i=1 to 1000
        put cd fld "test"+1 into cd fld "test"
      end repeat
      unlock screen
      put (the ticks-s)
    end mouseUp
    

    「画面をロックしない場合」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
    
    on mouseUp
      put the ticks into s
      put 0 into cd fld "test"
      repeat with i=1 to 1000
        put cd fld "test"+1 into cd fld "test"
      end repeat
      put (the ticks-s)
    end mouseUp
    

     それぞれ5回ずつ実行した平均値は、「画面をロックする場合」が93、「画面をロックしない場合」は805.7となりました。つまり、画面をロックしたことでスピードが8.7倍になったことになります。



    ■フィールドの代わりに変数を使う
     フィールドに書き込むという事は、すなわちハードディスクへのアクセスがあるということですから、これを変数(メモリ)に変えることで大幅なスピードアップが図れます。
     フィールドを使うと時間がかかるのは、ハードディスクへのアクセスだけではなく、画面の書き換えも関係しています。表示されているフィールドに次々とデータを書き込むと、当然の事ながら画面上でもデータを書き換えるわけですから、時間がかかるのです。

     「画面をロックしない」のスクリプトを変更して実験をしてみたいと思います。フィールドの代わりに変数「test」を用いた場合と、フィールド「test」を使った場合です。
     結果は、「変数を使う場合」が14.3、「フィールドを使う場合」は575.3となりました。今回は40.2倍のスピードアップです。



    ■フィールドはhideして使う
     どうしてもフィールドを使わなければならない時は、hideして使うことでスピードを上げることができます。
     「画面をロックしない」のスクリプトを用いて実験をした結果、「フィールドをhideして使う」が426.3、「フィールドをshowして使う」が805.7ですので、1.89倍のスピードアップとなりました。



    ■カードを移動するのでなく、参照する
     他のカードにあるフィールドからデータを取る場合、カードを移動してデータを取ってくるより、参照した方が高速となります。

  • put cd fld "score" of cd "data"

     しかし、同じカードの複数のフィールドからデータを取る場合は少し状況が変わります。もし10以上のフィールドからデータを取ってくるような場合は移動してからまとめてデータを取った方が高速になるでしょう。あともう一つ、他のスタックのカードからデータを取ってくるような場合は、移動してデータを取ってくるしか方法はありません。
     これらのようにどうしても移動が必要な場合は画面をロックしておくことを勧めます。なぜなら、ユーザーにその画面を見せる必要がないですし、スピードも大幅に向上できるからです。



    ■変数名を短くする
     変数名を短くするだけでスピードは速くなります。あるスクリプトを用いて実験してみます。適当なボタンを2つ用意して、それぞれ以下のスクリプトを打ち込んでみて下さい。

    「変数名が短い場合」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    11 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put i into a
        put a*a into b
        put b/a into c
        put a*b*c into d
        put a/b/d into e
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「変数名が長い場合」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    11 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put i into SpeedTest_first
        put SpeedTest_first*SpeedTest_first into SpeedTest_second
        put SpeedTest_second/SpeedTest_first into SpeedTest_third
        put SpeedTest_first*SpeedTest_second*SpeedTest_third into SpeedTest_four
        put SpeedTest_first/SpeedTest_second/SpeedTest_four into SpeedTest_fifth
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「変数名が短い場合」が89.2、「変数名が長い場合」98.2はとなりましたので、変数名を短くしたことでスピードが10%アップしたことになります。
     変数を短くすればスピードは早くなるのですが、その代わりに変数名がわかりにくくなりますので、ほどほどにするのが良いでしょう。



    ■文字列はダブルクォーテーションでくくる
     文字列をダブルクォーテーションでくくることでもスピードアップを図ることができます。
     文字列は、変数と同じ名前でない場合はダブルクォーテーションを付けなくてもエラーにはなりません。そのため、文字列をそのままスクリプト内に記述してしまうことが多いのですが、ダブルクォーテーションでくくることにより、変数と区別されるのです。
     もしダブルクォーテーションでくくらなかったとしたら、まず変数にデータが納められているかどうかを調べてから、文字列として扱うことになります。すなわち、これによりHyperCardは最初から正しいアクセスができるわけです。

    「文字列をダブルクォーテーションでくくる」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put NumToChar(random(255)) into a
        put "HyperCard"&a into a
        put "Apple"&a into b
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「文字列をダブルクォーテーションでくくらない」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put NumToChar(random(255)) into a
        put HyperCard&a into a
        put Apple&a into a    
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「文字列をダブルクォーテーションでくくる」が203.3、「文字列をダブルクォーテーションでくくらない」は218.3となりましたので、7%のスピードアップとなりました。



    ■イコール(等しい)でなく、ノットイコール(等しくない)を使う
     これは非常に不思議なのですが、イコールを使うよりノットイコールを使った方が、少しですが早くなります。

    「ノットイコールを使う」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    
    on mouseUp
      put the ticks into s
      put 0 into i
      repeat while i<>1000
        put i+1 into i
        put random(100) into a
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「イコールを使う」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    
    on mouseUp
      put the ticks into s
      put 0 into i
      repeat until i=1000
        put i+1 into i
        put random(100) into a
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「ノットイコールを使う」が46.7、「イコールを使う」は45となりましたので、3%のスピードアップとなりました。



    ■関数は the xxxx を使う
     HyperCardの関数の呼び出しは、theを使う方法と、()を使う方法の2通りがあります。この時、theを使って記述するとスピードが向上します。

     theを使うと、呼び出しは直接HyperCardに送られるのですが、()を使うと、全てのメッセージの階層を通って同じ名前の関数がないかどうかをチェックした後でHyperCardに送られることになるため、スピードが遅くなってしまうのです。

    「the xxxxを使う」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put the random of 100 into a
        put a&the secs&the ticks into b
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「()を使う」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put random(100) into a
        put a&secs()&ticks() into b
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「theを使う」が195.3、「()を使う」は216となりましたので、11%のスピードアップとなりました。



    ■複数の式をまとめる
     スクリプトをわかりやすくするために、複雑な式を必要とする場合は式の値を変数などに納めてから計算をするという方法を用います。しかし、この方法は2つの問題点があります。まず、1つ目は変数が多くなってしまうということ、2つ目はスクリプトが長くなってしまうという事です。ですから、まとめることができる式はできるだけまとめるようにしましょう。

    「複数の式をまとめる」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put random(100)+random(100)+random(100) into a
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「変数に一度代入させる」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put random(100) into a
        put random(100) into b
        put random(100) into c
        put a+b+c into d
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「複数の式をまとめる」が53、「変数に一度代入する」は99.7となりましたので、19%のスピードアップとなりました。



    ■変数の参照を少なくする
     具体的を用いて説明します。変数xをyだけ増加させようという場合、

    1 put x + y into x

    より

    2 add y to x

    とする方が速いのです。
     なぜならば、1ではxを2回とyを1回、計3回変数を参照しますが、2ならばx、yを1回ずつの2回しか参照しないからです。

    「変数の参照を少なくする」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        add y to x
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「普通に変数の参照を行う」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put x + y into x
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「変数の参照を少なくする」が15、「普通に変数の参照を行う」は16.7となりましたので、10%のスピードアップとなりました。



    ■ループ構造の中は短くする
     ループは何回も同じ事を繰り返すわけですから、ループ構造内はできるだけ簡潔に記述するべきです。一行でも無駄な行があれば、繰り返しの回数だけ無駄な行を実行してしまうことになるからです。

    「ループ構造の中は短くする」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put the ticks into s
      put 1 into a
      put 2 into b
      put 0 into c
      repeat with i=1 to 1000
        put a+b+c into c
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「普通にループ構造内を記述する」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put the ticks into s  
      put 0 into c
      repeat with i=1 to 1000
        put 1 into a
        put 2 into b
        put a+b+c into c
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「ループ構造の中は短くする」が19.7、「普通にループ構造内を記述する」は40.3となりましたので、2.1倍のスピードアップとなりました。



    ■ハンドラからハンドラを呼び出さない
     長いスクリプトの場合、一部分をまとめてハンドラとして独立させることで、スクリプトが読みやすくなります。これはスクリプト作成の点からすると非常に有効にスクリプトを利用できる方法なのですが、その分実効速度は遅くなってしまうのです。
     それは、HyperCardでは、ハンドラを呼び出すとそのスクリプトのハンドル部をメモリに蓄え、スクリプトを実行後そのスクリプトのハンドル部を消去するようになっているからです。このような原因から、ハンドラからハンドラを呼び出すよりすべてハンドラの中だけで処理した方が高速化するというわけなのです。

    「ハンドラからハンドラを呼び出さない」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
    
    on mouseUp
      put the ticks into s
      repeat with i=1 to 1000
        put random(100) into a
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「ハンドラを呼び出す」
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put the ticks into s  
      put 0 into c
      repeat with i=1 to 1000
        put 1 into a
        put 2 into b
        put a+b+c into c
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「ハンドラからハンドラを呼び出さない」が26.3、「ハンドラを呼び出す」は67.3となりましたので、なんと2.6倍ものスピードアップとなりました。



    ■lineマcharマitemマwordの順に利用する
     コンテナ内の値を構成する単位をチャンクといいますが、このチャンクを変えることによりスピードアップを図ることができます。スピードはタイトルの通り、lineマcharマitemマwordの順になっています。

    「lineチャンクを用いる」
     1 
     2 
    
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put "1"&return&"2"&return&"3"&return&"4"&return&"5" into a
    
      put the ticks into s
      repeat with j=1 to 200
        repeat with i=1 to 5
          put line i of a into data
        end repeat
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「charチャンクを用いる」
     1 
     2 
    
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put "12345" into a
      
      put the ticks into s
      repeat with j=1 to 200
        repeat with i=1 to 5
          put char i of a into data
        end repeat
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「itemチャンクを用いる」
     1 
     2 
    
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put "1,2,3,4,5" into a
      
      put the ticks into s
      repeat with j=1 to 200
        repeat with i=1 to 5
          put item i of a into data
        end repeat
      end repeat
      put (the ticks-s)
    end mouseUp
    

    「wordチャンクを用いる」
     1 
     2 
    
     3 
     4 
     5 
     6 
     7 
     8 
     9 
    10 
    
    on mouseUp
      put "1 2 3 4 5" into a
      
      put the ticks into s
      repeat with j=1 to 200
        repeat with i=1 to 5
          put word i of a into data
        end repeat
      end repeat
      put (the ticks-s)
    end mouseUp
    

     「lineチャンクを用いる」が24.2、「charチャンクを用いる」は28.6、「itemチャンクを用いる」が32、「wordチャンクを用いる」は48.4となりました。スピードの向上を望むならば、lineチャンクを用いるのが良いでしょう。



    ■日本語を表示しないフィールドは英数字フォントを使う
     日本語を表示しないフィールドは英数字フォントに設定することで少しですが、スピードアップさせることができます。「画面をロックしない」のスクリプトを用いて実験をしてみます。

     結果は、フォントを「Osaka」にした場合が573、「Courier」にした場合が555.3となりました。3%のスピードアップが図れたことになります。



    ■その他
     この他にも、global宣言は一行で行う方が速い、オブジェクトの座標はrectよりlocの方が速いなどがあります。