【IRIS/Cache】グローバルざっくり解説#2 2回目の実行は何でこんなに早いの?

本記事は、グローバルアクセスとグローバル・バッファの動作について解説致します。

※この記事は下記の方向けになります。
  • ObjectScript初心者の方
  • グローバル構造について詳しく知りたい方
  • グローバル・バッファ(データベース・キャッシュ)について知りたい方

Q. 2回目の実行は何でこんなに早いの?

A. 1回目に実行したグローバルが、メモリ上にキャッシュされるため。

実際に速度を検証してみましょう。

下記処理をターミナル上で2回実行してみます。

ClassMethod blockCheck()
{
	s start = $zh
	
	// No.29842 データ・ブロックより
	s txt = ^BlockSearch(1)
	s txt = ^BlockSearch(2)
	
	// No.465390 データ・ブロックより
	s txt = ^BlockSearch(162820)
	s txt = ^BlockSearch(163022)
	
	// No.29842 データ・ブロックより
	s txt = ^BlockSearch(203)
	
	w $zh-start
}

グローバル・バッファを削除するため、一端IRISを再起動してからターミナルで実行します。

1回目と2回目の実行速度差
  1. 1回目 = 0.026168
  2. 2回目 = 0.000026

処理結果を見てみると、2回目は1回目と比較して、およそ1,000倍速く処理が完了しています。

この結果だけを見ると、実に驚異的な速度差にですよね。

この速度差は、1回目の処理でメモリ上にグローバルがキャッシュされた事が要因となっています。

本記事では、このグローバル・バッファについて解説したいと思います。

グローバルのキャッシュはブロック単位

ObjectScriptではグローバルを取得した際、「ブロック内の全データ」をメモリにキャッシュします。

そのため、同じブロック内のグローバルを取得する際は、ディスクアクセスを行わず、グローバル・バッファからデータを取得するため、データ取得処理が高速になります。

先ほどの処理と各ブロックの関係性を確認してみます。

処理の流れ
s txt = ^BlockSearch(1)    ← No.29842
s txt = ^BlockSearch(2)    ← No.29842
s txt = ^BlockSearch(162820) ← No.465390
s txt = ^BlockSearch(163022) ← No.465390
s txt = ^BlockSearch(203)   ← No.29842

詳細は「グローバルざっくり解説#1 ブロックって何?」を参照してください。

このブロック構成をイメージしつつ、グローバルキャッシュの動作を確認してみましょう。

各処理とキャッシュの関連性を確認する

先ずは、各処理でディスクへのアクセス数とキャッシュへのアクセス数を、1回目と2回目の実行で比較してみます。

1回目の実行

下記表は、1回目の計測結果をまとめました。
関数「blockCheck()」の処理行(ライン行)毎に、各ブロックへのアクセス回数を記しています。

処理時間Disc
Dir
Disc
Upn
Disc
Bpn
Disc
Data
Buf
Dir
Buf
Upn
Buf
Bpn
Buf
Data
^BlockSearch(1)0.02192301111000
^BlockSearch(2)0.00000600000001
^BlockSearch(162820)0.00718500010110
^BlockSearch(163022)0.00001000000001
^BlockSearch(203)0.00000600000001
Disc=ディスクアクセス, Buf=グローバル・バッファ
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック

※計測のため、各処理は通常の処理よりも遅くなっています。

上記表から、最初のグローバル(^BlockSearch(1))取得は「ディスクアクセスが一番多い」「一番処理が遅い」事が分ります。

表だけではイメージしにくいので、処理の流れを図解してみます。

これらの動作から、「ブロック」単位でメモリにキャッシュしている事が確認できます。

また、ディスクアクセスと比較して、グローバル・バッファへのアクセスがいかに高速に行われるかも分かります。

2回目の実行

では、本題の2回目の処理が早い理由を確認します。

1回目と同様に、各ブロックへのアクセス数をカウントしてみましょう。

処理時間Disc
Dir
Disc
Upn
Disc
Bpn
Disc
Data
Buf
Dir
Buf
Upn
Buf
Bpn
Buf
Data
^BlockSearch(1)0.00000900000001
^BlockSearch(2)0.00000200000001
^BlockSearch(162820)0.00000500000001
^BlockSearch(163022)0.00000200000001
^BlockSearch(203)0.00000500000001
Disc=ディスクアクセス, Buf=グローバル・バッファ
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック

全てのグローバルをグローバル・キャッシュ、しかもデータ・ブロックのみから取得しているので、高速で処理が終わっている事が分ります。

また表より、^BlockSearch(2)は^(1)より処理が早く、^(163022)は^(162820)より処理が早い事が分かります。

これは、同ブロック内のデータを連続で取得している事が理由と考えています。
 → これに関しては、別の記事にて記載します。

他のプロセスではどうか?

グローバル・バッファはプロセス固有ではないので、他のプロセスも同様に処理が早くなるはずです。

検証してみましょう。

処理時間Disc
Dir
Disc
Upn
Disc
Bpn
Disc
Data
Buf
Dir
Buf
Upn
Buf
Bpn
Buf
Data
^BlockSearch(1)0.00009000001111
^BlockSearch(2)0.00000300000001
^BlockSearch(162820)0.00001500000111
^BlockSearch(163022)0.00000400000001
^BlockSearch(203)0.00000600000001
Disc=ディスクアクセス, Buf=グローバル・バッファ
Dir=ディレクトリ・ブロック, Upn=上部ポインタ・ブロック, Bpn=下部ポインタ・ブロック, Data=データ・ブロック

この検証により、下記2点が分かります。

  • グローバル・バッファは、プロセス固有ではない。
     → 各ブロックへのアクセスが、全てグローバル・バッファにあるブロックに対して行われているため
  • ディレクトリ・ブロックからの参照は、プロセス固有情報である。
     → 最初のアクセス時に、ディレクトリ・ブロック、上部/下部ポインタ・ブロックを参照しているので、これらのブロック参照方法は「プロセス固有情報」と推測できる

数回別のプロセスで試しましたが、同様の挙動となっています。

また、どのプロセスで検証しても、2回目の実行は全てデータ・ブロックのみを参照しています。

グローバル・バッファの設定方法

様々なグローバルをメモリ上に展開したい場合は、グローバル・バッファを多めに設定した方がいいと思います。
 ※ 設定は管理ポータル画面の下記で行います。

まとめ

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

まとめ
  • グローバルを取得すると、ブロック単位でメモリにキャッシュされる
  • 以降、同ブロック内のグローバルは、キャッシュされたデータを取得する
  • 同ブロック内のグローバルを連続で取得する場合は、さらに早くなる
  • グローバル・キャッシュは、他のプロセスからも取得できる
  • データ・ブロックへのアクセス方法は、プロセス固有情報と思われる

以上、グローバルおよびキャッシュの挙動について解説しました。
本記事が、皆さまの実務や学びの中で、少しでもお役に立てれば幸いです。