
本記事は、ブロックの構造と$orderの挙動について解説します。
Q. $orderの降順は何でこんなに遅いの?
A. ブロックの構造が、$orderの降順に適していない為。
ブロックの構造?
と言われても、なかなかピンとは来ないと思います。
説明の前に、一端データ・ブロックの情報を確認してみます。
【ブロック情報】
Block # 29842 Type: 8 DATA
Link Block: 29844 Offset: 7352
…略
この「298844」の値は、次のノードが格納されたデータ・ブロックの番号になります。
今までのブロック構成図に「Link Block」の値を加えてみます。
※下部ポインタ・ブロック=6個、データ・ブロック=4,983個

この図より、下部ポインタ・ブロック、データ・ブロックは、次のブロックのブロック番号を持っている事が分ります。
また、Link Block = 0のブロックは、その集団の最後のブロックである事を示しています。
ここから分かる事は、「次のブロックは把握しているが、前のブロックは把握していない」と言うことです。
つまり、「$order昇順はLink Blockを辿る事で、素早くデータを取得する事が出来る」のに対し、「$order降順は前のブロックを取得する手段が無いため、遅くなる」と言えます。
では、確認してみましょう!
動作確認
$orderの動作を確認するため、簡単なPGを作成して確認してみます。
ClassMethod loopData(dir As %String)
{
s start = $zh
s key = ""
f {
s key = $o(^BlockSearch(key),dir)
q:key=""
}
w $zh - start
}
検証前にIRISを再起動し、$orderの昇順と降順での動作の差を確認してみます。
$order「昇順」
各ブロックのアクセス回数を表にしています。
処理 | 時間 | Disc Dir | Disc Upn | Disc Bpn | Disc Data | Buf Dir | Buf Upn | Buf Bpn | Buf Data |
---|---|---|---|---|---|---|---|---|---|
^BlockSearch(key) | 2.897492 | 0 | 1 | 1 | 4983 | 1 | 0 | 0 | 1 |
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック
データ・ブロックの数が4,983個なので、ディスクアクセス回数と一致しています。
下部ポインタブロックは6個存在していますが、アクセス回数は1回です。
これより、最初の下部ポインタ・ブロックからデータ・ブロックにアクセスした後、データ・ブロックを連続でアクセスしていったと推測できます。

動作に無駄がないですね。
2回目の処理はこんな感じになります。
処理 | 時間 | Disc Dir | Disc Upn | Disc Bpn | Disc Data | Buf Dir | Buf Upn | Buf Bpn | Buf Data |
---|---|---|---|---|---|---|---|---|---|
^BlockSearch(key) | 0.954625 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 4984 |
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック
ディスクアクセスではなく、グローバル・バッファから取得している事が分ります。
$order「降順」
では、本題の$order降順の動作確認になります。
各ブロックのアクセス回数を表にしています。
処理 | 時間 | Disc Dir | Disc Upn | Disc Bpn | Disc Data | Buf Dir | Buf Upn | Buf Bpn | Buf Data |
---|---|---|---|---|---|---|---|---|---|
^BlockSearch(key) | 10.605137 | 0 | 1 | 6 | 4983 | 1 | 4982 | 4977 | 1 |
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック
上部ポインタ・ブロック(Upn)のDiscとBufを足すと(1 + 4,982)、4,983回となります。
同様に、下部ポインタ・ブロック(Bpn)のDiscとBufを足すと(6 + 4,977)、4,983回です。
この4,983回は、データ・ブロックの個数と一致しています。
ブロックは、次のブロック(Link Block)を知っていても、前のブロックを知りません。
そのため、参照しているデータ・ブロックからデータの取得が終わったら、次のグローバルを取得する為に「上部ポインタ・ブロック」まで戻らないと、目的のデータ・ブロックの番号が分りません。
つまり、毎回「上部ポインタ・ブロックからデータ・ブロックへの移動を繰り返す」と推測できます。

この動作を見る限り、確かに$order降順が遅い理由が分かります。
ちなみに2回目の処理では、下記になります。
処理 | 時間 | Disc Dir | Disc Upn | Disc Bpn | Disc Data | Buf Dir | Buf Upn | Buf Bpn | Buf Data |
---|---|---|---|---|---|---|---|---|---|
^BlockSearch(key) | 2.627983 | 0 | 0 | 0 | 0 | 0 | 4983 | 4983 | 4985 |
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック
全ブロックが、バッファ上のデータを参照しているので流石に早いですが、動作に関しては変わっていないですね。
データ・ブロックのアクセス回数が2回多いのは・・・何ででしょう。
$oreder終了時のnullで1回として、後1回は・・・?
誰かご存じの方は、ひっそり教えていただけると幸いです。
グローバルアクセス以外での挙動
グローバルアクセスでの$order降順の動作は確認しました。
では、他のデータでの動作はどうなるでしょうか。
下記2つを確認してましょう。
変数
ターミナルを使用して、変数での$order昇順/降順の速度差を確認してみます。
ターミナルにて下記を実行し、処理速度を比較してみます。
k data
f pos=1:1:1000000 s data(pos)=""
chkLoop(dir)
f cnt=1:1:10 {
s start=$zh
s pos=""
f { s pos=$o(data(pos),dir) q:pos="" }
w !,$zh-start
}
d chkLoop(1)
d chkLoop(-1)
結果をまとめてみます。
1回 | 2回 | 3回 | 4回 | 5回 | 6回 | 7回 | 8回 | 9回 | 10回 | 平均 | |
---|---|---|---|---|---|---|---|---|---|---|---|
昇順 | 0.355 | 0.391 | 0.345 | 0.338 | 0.356 | 0.335 | 0.381 | 0.314 | 0.261 | 0.351 | 0.343 |
降順 | 0.295 | 0.246 | 0.301 | 0.273 | 0.283 | 0.273 | 0.243 | 0.278 | 0.230 | 0.276 | 0.270 |
あれ?
降順の方がわずかに早いんですが・・・
何回やっても、わずかに降順の方が早いですね。
あー、うん。
ともかく、変数では、昇順/降順を気にする必要は無さそうです!
プロセス・グローバル
同様に、ターミナルにて下記を実行し、処理速度を比較してみます。
k data
f pos=1:1:1000000 s ^||data(pos)=""
ClassMethod loopPData(dir As %String)
{
s start=$zh
s pos=""
f {
s pos=$o(^||data(pos),dir)
q:pos=""
}
w !,$zh-start
}
結果をまとめてみます。
1回 | 2回 | 3回 | 4回 | 5回 | 6回 | 7回 | 8回 | 9回 | 10回 | 平均 | |
---|---|---|---|---|---|---|---|---|---|---|---|
昇順 | 0.531 | 0.402 | 0.420 | 0.492 | 0.480 | 0.483 | 0.413 | 0.359 | 0.468 | 0.484 | 0.453 |
降順 | 6.240 | 4.983 | 5.819 | 4.566 | 5.053 | 6.051 | 6.154 | 5.858 | 5.905 | 5.873 | 5.650 |
今回は、結果がはっきり分かれました。
降順は明らかに遅いですね。
【プロセス・グローバルでの$order降順】
下記は、各ブロックへのアクセス回数です。
処理 | 時間 | Disc Dir | Disc Upn | Disc Bpn | Disc Data | Buf Dir | Buf Upn | Buf Bpn | Buf Data |
---|---|---|---|---|---|---|---|---|---|
^||data(pos) | 6.998599 | 0 | 0 | 0 | 0 | 0 | 1090 | 1090 | 1092 |
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック
グローバル・バッファも、上/下ポインタ・ブロック、データ・ブロックに分かれているようです。
プロセス・グローバルも通常のグローバルも、仕様が変わらないようですね。
であるなら、この結果も納得がいきます。
まとめ
これまでの結果をまとめると下記になります。
以上、$orderの挙動について解説しました。
本記事が、皆さまの実務や学びの中で、少しでもお役に立てれば幸いです。