by 野本夏俊
例えば、「あるテキストデータに対して1文字ごとにスペースを挿入する」というスクリプトを組む場合、set AppleScript's text item delimiters to ""
set R to {}
repeat with i from 1 to length of MyData ------------*
set end of R to character i of MyData & " "
end repeat ---------------------------------------**
R as textというようにすると思います。(まぁ、いろいろやり方はあると思いますけど...)
MyDataが小さなデータ(100文字以内など)ならこれで特に問題はありませんが、大きくなる(数千を超える)と、問題が出てきます。例えば、以下のようなスクリプトで実験して見ます。----------テスト1
テスト環境:
マシン:UMAX Pulsar 2250 メモリ割り当て20MB
OS :7.6.1+SpeedDoubler2property S : 1000
property E : 10000
property Step : 1000on open FS
set AppleScript's text item delimiters to return
repeat with F in FS
Doit(F)
end repeat
end openon Doit(F)
open for access F
set MyData to read F
close access F
set TheTime to {}
repeat with T from S to E by Step
set LastTime to current date
set R to {}
repeat with i from 1 to T ----------------------------*
set end of R to character i of MyData & " "
end repeat ---------------------------------------**
set end of TheTime to (T as string) & tab & ((current date) - LastTime) as string
end repeat
set fref to open for access file ((F as text) & ".time") with write permission
set eof fref to 0
write (TheTime as text) to fref
close access fref
end Doitテキストデータを読み込んで、処理する文字数を千〜1万文字まで千ずつ増やしながら、処理時間を記録するスクリプトです。結果は以下のようになります。
文字数 時間(秒)
1000........2
2000........5
3000.......11
4000.......18
5000.......28
6000.......40
7000.......55
8000.......70
9000.......87
10000.....109これをグラフ化して見ればわかりますが、文字数が増えるに連れて、処理時間が加速度的に増えていきます。1000と2000の時間差が3秒ですから、1000文字増えるごとに3秒ずつ加算されていくなら、10000では27秒で処理できてもよさそうなもんですが、実際はそうはなりません。
これはアップルスクリプトでは(他でも?)「文字1と文字nを求める時間が違う」ことが原因です。試しに、以下のようなスクリプトで文字nを求める時間を計測して見ます。property S : 1
property E : 10001
property Step : 1000on open FS
set AppleScript's text item delimiters to return
repeat with F in FS
Doit(F)
end repeat
end openon Doit(F)
open for access F
set MyData to read F
close access F
set TheTime to {}
repeat with T from S to E by Step
set LastTime to current date
repeat 50000 times
get character T of MyData
end repeat
set end of TheTime to (T as string) & tab & ((current date) - LastTime) as string
end repeat
set fref to open for access file ((F as text) & ".R") with write permission
set eof fref to 0
write (TheTime as text) to fref
close access fref
end Doitテキストデータを読み込んで、nを1〜10001まで千ずつ増やしながら、文字nを50000回求める時間を記録するスクリプトです。結果は以下のようになります。
n 時間
1..........4
1001......11
2001......18
3001......25
4001......32
5001......38
6001......45
7001......52
8001......57
9001......66
10001.....74結果をグラフ化すればわかりますが、ほぼnに比例して処理時間が増加しています。テスト1ではこの違いがもろに処理時間に反映されています。
では、これを回避するためにテスト1の*〜**を
repeat with Block from 1 to T - 999 by 1000 --------------*
set R to R & SUB(text Block thru (Block + 999) of MyData)
end repeat ----------------------------------------**と、書き換え、サブルーチンとして、
on SUB(SubData)
set SubR to {}
repeat with i from 1 to 1000
set end of SubR to character i of SubData & " "
end repeat
return SubR
end SUBを加えます。-------------テスト2
つまり、データを1000ずつのブロックに分けて処理するわけです。この結果、最終的に得られるRの値は同じですが、処理時間は大きく異なります。
テスト1 テスト2
回数.......時間 回数.......時間
1000..........2 1000..........2
2000..........5 2000..........3
3000.........11 3000..........5
4000.........18 4000..........6
5000.........28 5000..........7
6000.........40 6000.........10
7000.........55 7000.........10
8000.........70 8000.........13
9000.........87 9000.........13
10000.......109 10000........16このように大きなデータを処理するときには、データをある程度の大きさにブロック分けして処理するほうが効率がいいことがわかります。
ある程度経験を積んだスクリプターにとっては目新らしいTipsではないでしょうが、ま、初心者向けということで... ちょっと参考になりました?
さらに続きです。下のようなスクリプトを組んで、ブロックわけするときには、ブロックサイズをどの程度の大きさにするのがベストなのかを調べて見ました。
property BlockStart : 100
property BlockEnd : 1000
property BlockStep : 100on open FS
set AppleScript's text item delimiters to return
repeat with F in FS
Doit(F)
end repeat
end openon Doit(F)
open for access F
set MyData to read F
close access F
set TheTime to {}
repeat with BlockSize from BlockStart to BlockEnd by BlockStep
set LastTime to current date
set R to {}
repeat with Block from 1 to 10001 - BlockSize by BlockSize
set R to R & SUB(text Block thru (Block + BlockSize - 1) of MyData, BlockSize)
end repeat
set end of TheTime to (BlockSize as string) & tab & ((current date) - LastTime) as string
end repeat
set fref to open for access file ((F as text) & ".T5") with write permission
set eof fref to 0
write (TheTime as text) to fref
close access fref
end Doiton SUB(MyData, BlockSize)
set R to {}
repeat with i from 1 to BlockSize
set end of R to character i of MyData & " "
end repeat
return R
end SUB結果的に、1ブロックを200文字前後にするのが最も速いことがわかりました。
以外に細かく分けたほうがいいんですね。
で、テスト2のスクリプトのブロックサイズを200にして試した結果...文字数 ブロック ブロック ブロック
なし 1000 200
----------------------------------
1000 2 2 0
2000 5 3 2
3000 11 5 1
4000 18 6 3
5000 28 7 3
6000 40 10 4
7000 55 10 5
8000 70 13 5
9000 87 13 6
10000 109 16 8ブロック分けするだけで、109秒かかっていたものが8秒まで短縮できてしまうんですから、なかなかたいしたものです。
処理の内容によって最適なブロックサイズは変わってくると思いますから、いろいろ試行錯誤して見ましょう。チューニング次第でずいぶんスピードが改善されるかもしれませんよ。