Advertisement
Guest User

Untitled

a guest
Oct 14th, 2019
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.64 KB | None | 0 0
  1. もう使わないので置いておく
  2.  
  3.  
  4. 高速化のアイデアはいずれもどの程度速くなるのか調べていない。可読性とのトレードオフ
  5.  
  6.  
  7. # `@tmp` suffix
  8.  
  9. oomSESTのソースをデコンパイルするとある時期から`@tmp`という接尾辞がついた変数が使われるようになったことがわかる。これは次のルールで運用していた。
  10.  
  11. `@tmp`がついた変数が有効なうちは`gosub`、`goto`、ユーザー定義関数の呼び出しをおこなってはならない。逆に言えば、それらを呼んだ後は`@tmp`変数の値が変更されている可能性がある。
  12.  
  13. このルールを守ることで擬似的にローカル変数を使うことが可能になり、ネストした関数呼び出しの中で変数名がかぶってバグる可能性を減らせる。
  14.  
  15.  
  16. # `swap@tmp`
  17.  
  18. 変数のスワップで出てくるこのイディオム
  19.  
  20. ```
  21. z = x
  22. x = y
  23. y = z
  24. ```
  25.  
  26. は一つ問題を抱えている。それは、もし別の場所で`z`という変数を定義していると意図せずして`z`が書き換わることである。これを避けるには他で使われていない一意な名前をつけるか上書きしても問題ない変数を使う必要がある。前者は変数がいたずらに増えてしまう。後者は上書きしていいかどうかを調べるコストが高くつく(その上今は上書きしていい変数だったとしても将来においてそうとは限らない)。この問題の解決のためスワップ専用の変数をあらかじめ用意しておき、常にそれを使うようにする。
  27.  
  28. ```
  29. #define swap(%1, %2) swap@tmp = %1: %1 = %2: %2 = swap@tmp
  30.  
  31. swap x, y
  32. ```
  33.  
  34. これは`@tmp`の典型的な利用例でもある。当然この変数はこのマクロを通さずして参照してはならない。
  35.  
  36.  
  37. # Pre-parsed custom file
  38.  
  39. カスタムファイルの読み込みは高コストな処理である。起動時間を速めるため、一度パースしたあとのファイルをバイナリにデシリアライズしてファイルにダンプしておく。もしもカスタムファイル読み込み時にキャッシュが見つかればそちらを変わりに読む。うまくすれば10倍は速くなると踏んでいる。なお、大元のファイルが変更されたときはたとえキャッシュがあってもそれを使ってはならない。元のファイルの更新時間を取得してキャッシュファイルのそれと比較するか、元のファイルのハッシュ値をキャッシュファイルに埋め込んでおきハッシュ値が変わっていないことを確認するかすればよい。
  40.  
  41.  
  42. # Sorting algorithm
  43.  
  44. ほとんどの処理でバブルソートが使われているが、まともなソートに置き換える。バブルソートは安定なソートなので不安定なものにするとまずいかもしれない。
  45.  
  46.  
  47. # Faction
  48.  
  49. 高速化のためfactionを文字列でなく数値で保持する。読み込みと保存の直後/直前に文字列に変換すればいい。
  50.  
  51.  
  52. # Binary search
  53.  
  54. アイテムやキャラクタの情報を参照する際現状はif文の羅列により処理しているがこれを二分探索するように変える。O(n)からO(log n)。これらを手で書くのは狂気の沙汰であるから、適当にスクリプトを書いて生成する。当然データを配列に格納してインデックスでアクセスすればO(1)になるが使える場所はやや限られる。
  55.  
  56.  
  57. # `switch` without fall-through
  58.  
  59. HSPのswitchはマクロで実現されている。このマクロはfall-throughをサポートしているが、fall-throughしない大多数のswitch文では単に無駄な処理が行われるだけになる。fall-throughしたい典型的な利用例は
  60.  
  61. ```
  62. switch x
  63. case 1
  64. case 2
  65. // 1、2で共通の処理
  66. swend
  67. ```
  68.  
  69. だが(というよりこれ以外でfall-throughするのは99%バグ)、これは単に`|`を使えばいい。
  70.  
  71. ```
  72. if (x == 1 | x == 2) {
  73. }
  74. ```
  75.  
  76. なお、xが複雑な式だったり副作用を持つ式だったりしたときは直接参照するのではなく先に適当な変数に代入しておくべきである。
  77.  
  78. また、if文のelse節を使えばもう少し効率を上げられる。
  79.  
  80. ```
  81. if (x == 1) {
  82. }
  83. if (x == 2) {
  84. }
  85. if (x == 3) {
  86. }
  87.  
  88.  
  89. if (x == 1) {
  90. } else: if (x == 2) {
  91. } else: if (x == 3) {
  92. }
  93. ```
  94.  
  95. これを簡略化するため次のようなマクロが使える。
  96.  
  97.  
  98. ```
  99. #define elseif else:if
  100. ```
  101.  
  102. 信仰する宗派によっては`elseif`の代わりに`elif`や`elsif`を使いたいかもしれない。
  103.  
  104.  
  105. # Item/character generation
  106.  
  107. ランダムに生成されるアイテムやキャラで何を選ぶかはいくつかのパラメータと乱数で決まっているが、このパラメータのいくつかはほぼ特定のパターンしか使われない。更にいくつかのアイテムやキャラはランダム生成されることがない(ほとんどのユニークと固定AF)。`flttype`ごとに生成テーブルを分けるといった最適化が考えられる。
  108.  
  109.  
  110. # `lang`/`en`/`jp`
  111.  
  112. `lang`を連続して何度も呼んでいる箇所は`if (jp)`で分岐した方が速い
  113.  
  114. ```
  115. a = lang("あ", "A")
  116. b = lang("い", "B")
  117. c = lang("う", "C")
  118.  
  119.  
  120. if (jp) {
  121. a = "あ"
  122. b = "い"
  123. c = "う"
  124. } else {
  125. a = "A"
  126. b = "B"
  127. c = "C"
  128. }
  129.  
  130. // もしもa、b、cが連続した配列ならさらに速くできる。array(0)、array(1)、array(2)にそれぞれ代入するよりこの方が速い。
  131. if (jp) {
  132. array = "あ", "い", "う"
  133. } else {
  134. array = "A", "B", "C"
  135. }
  136. ```
  137.  
  138. # Multi-line definition
  139.  
  140. HSPでは一つの文は一行に書かねばならず、改行する手段はない。これはある程度大きな配列を代入したいとき問題になる。
  141.  
  142. ```
  143. array = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
  144. ```
  145.  
  146. エディタで折り返しを設定していても視認性は悪い。もし各要素が文字列であればさらに悲惨になる。これは次のようなイディオムで改善できる。
  147.  
  148. ```
  149. array(0) = 0, 1, 2, 3, 4
  150. array(5) = 5, 6, 7, 8, 9
  151. array(10) = 10, 11, 12, 13, 14
  152. ```
  153.  
  154. これは同じ結果を生む。しかしながらこの書き方だと配列のインデックスを書く必要がある。もしインデックスを間違えてもコンパイラは何も言わないためバグの原因になる。これを解決するための案を次に示す。
  155.  
  156. ```
  157. #define ARRAY \
  158. 0, 1, 2, 3, 4, \
  159. 5, 6, 7, 8, 9, \
  160. 10, 11, 12, 13, 14
  161.  
  162. array = ARRAY
  163. ```
  164.  
  165. `#define`マクロの定義部は幸運なことに改行できる。実用上は`ARRAY`のようなマクロをまとめた別ファイルを用意して`#include`するのがいいだろう。当然名前空間を汚染しないよう注意深く名付ける必要がある。例えば称号の日本語名であれば`ST_TITLE_NAME_JA`のようにする(`ST`はoomSESTで利用しているマクロ用のプレフィクス。名前のコンフリクト防止)。
  166.  
  167.  
  168. # Nested if
  169.  
  170. HSPの`&`や`|`はショートサーキットしない。もしそうしたいならifをネストさせる必要がある。
  171.  
  172. ```
  173. if (a & b) { // 部分式aとbは値によらず常に評価される
  174. }
  175.  
  176. if (0 <= i & i < length(array) & array(i) == 0) {
  177. // iの範囲をチェックしてからarrayにアクセスするつもりのコード。実際には範囲外だろうがarrayにアクセスしてエラーを吐く
  178. }
  179.  
  180. // 正しくはこう
  181. if (0 <= i & i < length(array)) {
  182. if (array(i) == 0) {
  183. }
  184. }
  185.  
  186. //またはこう
  187. if (0 <= i) {
  188. if (i < length(array)) {
  189. if (array(i) == 0) {
  190. }
  191. }
  192. }
  193. ```
  194.  
  195. この書き方はネストが際限なく深くなってしまう。また、コードリーディングのコストも高くつく。これを改善するため次のように書く方法がある。
  196.  
  197. ```
  198. if (0 <= i): if (i < length(array)): if (array(i) == 0) {
  199. // Do something.
  200. }
  201. ```
  202.  
  203. このイディオムをもう少し書きやすくするため次のようなマクロを考案した。
  204.  
  205. ```
  206. #define AND ):if(
  207. ```
  208.  
  209. これを使うと次のように書ける。
  210.  
  211. ```
  212. if (0 <= i AND i < length(array) AND array(i) == 0) {
  213. // Do something.
  214. }
  215. ```
  216.  
  217.  
  218. # Sliced scroll cache
  219.  
  220. ElonaのUIにはウィンドウの背景に羊皮紙が使われているが、これはいわゆる9-slice imageのような手法を用いていて描写回数が多い。これをウィンドウサイズごとにキャッシュしておけば速くならないか。
  221.  
  222.  
  223. # Layered rendering
  224.  
  225. マップ、アイテム、キャラ、HUDのそれぞれ専用のテクスチャを用意し、それぞれが更新されたときだけ描写し直す。更新されたかどうか監視するコストの方が高くなる可能性はある。
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement