【IRIS/Cache】グローバルざっくり解説#3 $orderの降順は何でこんなに遅いの?

本記事は、ブロックの構造と$orderの挙動について解説します。

※この記事は下記の方向けになります。
  • ObjectScript初心者の方
  • グローバル構造について詳しく知りたい方
  • $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.89749201149831001
Disc=ディスクアクセス, Buf=グローバル・バッファ
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.95462500000114984
Disc=ディスクアクセス, Buf=グローバル・バッファ
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.60513701649831498249771
Disc=ディスクアクセス, Buf=グローバル・バッファ
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.62798300000498349834985
Disc=ディスクアクセス, Buf=グローバル・バッファ
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.3550.3910.3450.3380.3560.3350.3810.3140.2610.3510.343
降順0.2950.2460.3010.2730.2830.2730.2430.2780.2300.2760.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.5310.4020.4200.4920.4800.4830.4130.3590.4680.4840.453
降順6.2404.9835.8194.5665.0536.0516.1545.8585.9055.8735.650

今回は、結果がはっきり分かれました。
降順は明らかに遅いですね。

【プロセス・グローバルでの$order降順】

下記は、各ブロックへのアクセス回数です。

処理時間Disc
Dir
Disc
Upn
Disc
Bpn
Disc
Data
Buf
Dir
Buf
Upn
Buf
Bpn
Buf
Data
^||data(pos)6.99859900000109010901092
Disc=ディスクアクセス, Buf=グローバル・バッファ
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック

グローバル・バッファも、上/下ポインタ・ブロック、データ・ブロックに分かれているようです。
プロセス・グローバルも通常のグローバルも、仕様が変わらないようですね。

であるなら、この結果も納得がいきます。

まとめ

これまでの結果をまとめると下記になります。

まとめ
  • グローバルでの$orderは、ブロックの構造上、降順が遅くなるのは仕方がない
  • 変数では、$orderの昇順/降順に差がない
  • プロセス・グローバルは、ディスクアクセスが無いだけで、通常のグローバルと動作が同じになる

以上、$orderの挙動について解説しました。
本記事が、皆さまの実務や学びの中で、少しでもお役に立てれば幸いです。