VIのメモリ使用



LabVIEW 2018ヘルプ


発行年月: 2018年3月
製品番号: 371361R-0112
製品情報を参照

ダウンロード (Windowsのみ)


LabVIEW 2016ヘルプ
LabVIEW 2017ヘルプ
LabVIEW 2018ヘルプ
LabVIEW 2019ヘルプ
LabVIEW 2020ヘルプ

LabVIEWは、テキストベースのプログラミング言語で処理する必要がある多くの詳細な処理を行います。テキストベースの言語の最も重要な問題の1つにメモリ使用量が挙げられます。テキストベースの言語では、プログラマーはメモリを使用する前にメモリを割り当て、メモリの使用を終了する際メモリの割り当て解除を行う必要があります。また、最初に割り当てたメモリの終端を越える領域に書き込みしない注意が必要です。テキストベースの言語でのプログラマーによる最も大きなミスは、メモリの割り当てをしない、または十分なメモリを割り当てないことです。不十分なメモリの割り当てはデバッグの問題の原因ともなります。

LabVIEWのデータフローパラダイムは、メモリ管理の問題の大部分を解消します。LabVIEWでは、変数を割り当てたり、変数に値を割り当てません。その代わりに、データの転送を表す接続でブロックダイアグラムを作成します。

データを生成する関数によって、そのデータに対するストレージの割り当てが処理されます。データが使用されなくなると、関連するメモリが解放されます。新規情報を配列または文字列に追加すると、新しい情報を管理するために十分なメモリが自動的に割り当てられます。

この自動メモリ処理は、LabVIEWの主な利点の1つです。ただし、自動処理であるため、この操作の実行中に発生する動作の制御が制限されます。プログラムが大量のデータセットを処理する場合は、メモリの割り当てがいつ行われるかを認識することが重要になります。この操作の原則を理解することによって、より小さいメモリ必須条件のプログラムが作成できます。また、メモリ使用量を最小限にする方法を理解することによって、メモリの割り当てとデータのコピーの処理は非常に時間がかかるため、VIの実行速度を向上させることができます。

仮想メモリ

オペレーティングシステムでは仮想メモリの使用によって、アプリケーションが物理RAMで利用可能なメモリ以上のメモリにアクセスすることが可能になります。OSは物理RAMをページと呼ばれるブロックに分割します。アプリケーションまたはプロセスがアドレスをメモリブロックに割り当てる際、アドレスは物理RAMの直接的な場所ではなく、ページのメモリを参照します。OSは物理RAMとハードディスク間のこれらのページを交換します。

アプリケーションまたはプロセスが物理RAMに存在しないメモリの特定のブロックまたはページにアクセスする必要がある場合、OSはアプリケーションまたはプロセスにより使用されていない物理RAMのページをハードディスクに移動し、そのページを必要なページで置換することができます。OSはメモリ内のページの記録を保持し、アプリケーションまたはプロセスがそのメモリにアクセスする必要がある際、ページへの仮想アドレスを物理RAMの実アドレスに変換します。

以下の画像は、物理RAMの内外でページをスワップする2つの処理方法を示します。この例では、プロセスAおよびプロセスBが同時に実行されています。

1  プロセスA   3  プロセスB   5  プロセスBからのメモリページ  
2  物理RAM   4  仮想メモリのページ   6  プロセスAからのメモリページ  

アプリケーションまたはプロセスが使用するページ数は、利用可能な物理RAMではなくディスクの領域により異なるため、アプリケーションは物理RAMで実際に利用可能なメモリ以上のメモリを使用することができます。アプリケーションで使用されるメモリアドレスのサイズにより、アプリケーションがアクセス可能な仮想メモリの量が制限されます。

(Windows)LabVIEWの仮想メモリ使用量

LabVIEW(32ビット)は、32ビットアドレスを使用するLarge Address Awareです。32ビットWindowsでは、LabVIEW(32ビット)はデフォルトで最大2GBの仮想メモリにアクセスできます。Windowsのブート構成設定を変更して、LabVIEW(32ビット)を有効にし、最大3 GBの仮想メモリにアクセスできるようにします。64ビットWindowsでは、LabVIEW(32ビット)はデフォルトで最大4GBの仮想メモリにアクセスできます。

LabVIEW(64ビット)では、Windowsのバージョンによって最大8 TBまたは128 TBの仮想メモリにアクセスできます。LabVIEW(64ビット)が実際にアクセスできる仮想メモリのサイズは、物理的RAMのサイズおよびページファイルの最大サイズにも依存します。

VIコンポーネントのメモリ管理

VIには、以下の4つの主なコンポーネントで構成されています。

  • フロントパネル
  • ブロックダイアグラム
  • コード(マシンコードにコンパイルされるダイアグラム)
  • データ(制御器と表示器の値、デフォルトデータ、ダイアグラム定数データなど)

VIがロードされると、フロントパネル、コード(プラットフォームに対応する場合)、およびVIのデータがメモリにロードされます。プラットフォームまたはサブVIのインタフェースが変更されたためにVIの再コンパイルが必要な場合、ブロックダイアグラムが同様にメモリにロードされます。

また、VIはサブVIのコードおよびデータスペースをメモリにロードします。状況によっては、一部のサブVIのフロントパネルが同様にメモリにロードされる場合があります。たとえば、プロパティノードはフロントパネル制御器の状態に関する情報を操作するため、サブVIによりプロパティノードが使用される場合に起こります。

VIのコンポーネントの構成についての重要な点は、VIの一部をサブVIに変換する場合は通常メモリはそれほど使用されません。サブVIのないサイズの大きい単一のVIを作成する場合、メモリにトップレベルVIのフロントパネル、コード、データがロードされます。ただし、VIをサブVIに分割する場合、トップレベルVIのコードのサイズが縮小され、サブVIのコードとデータはメモリ内に常駐します。場合によっては、ランタイム時のメモリの使用量が減少します。

大きなVIは編集するのにも時間がかかります。この問題は、次の方法で回避することができます。

メモ  任意のVIのフロントパネルまたはブロックダイアグラムが画面よりも極めて大きい場合、サブVIへアクセスしやすくなるように、VIを分割するとよい場合があります。

データフロープログラミングとデータバッファ

通常、データフロープログラミングでは変数は使用しません。データフローモデルでは、通常ノードは消費データ入力および生成データ出力として記述されます。このモデルを実際に実装すると、大量のメモリを使用し、パフォーマンスの低いアプリケーションが作成されます。各関数は、出力が渡されるそれぞれの送信先にデータのコピーを生成します。LabVIEWでは、この実装がInPlace処理によって改善されます。

LabVIEWでは、InPlace処理によってメモリをいつ使用するか、また各出力端子のコピーを作成するかどうかを判断します。LabVIEWに入力から出力にデータがコピーされない場合、これらの入力と出力のデータはInPlaceとなります。

バッファ割り当てを表示ウィンドウを使用して、LabVIEWがデータのコピーを作成できる場所を認識することができます。

たとえば、従来のコンパイラの方法では、以下のブロックダイアグラムは入力と出力にそれそれデータブロックを使用し、計2つのデータブロックメモリを使用します。

入力配列および出力配列には同じ数の要素が含まれていて、両方の配列のデータタイプは同じです。受信する配列をデータのバッファと考えます。コンパイラは、出力に新しいバッファを作成する代わりに、入力バッファのメモリを出力バッファとして再利用します。このようなInPlace処理では、メモリ割り当てが実行時に必要ではなくなるため、メモリが節約され実行速度が向上します。

ただし、以下のブロックダイアグラムに示すように、コンパイラがすべての場合においてメモリバッファを再利用できるわけではありません。

1つの信号は、単一のデータソースを複数の送信先に渡します。「部分配列置換」関数は、入力配列から出力配列を生成します。この場合、関数の1つでデータがInPlaceとなり出力に入力配列のデータが再利用され、その他の関数ではInPlace処理が行われません。したがって、コンパイラは2つの関数で新規データバッファを作成し、配列データをバッファにコピーします。このブロックダイアグラムは、約12KB(元の配列に4KB、2つの追加データバッファにそれぞれ4KBずつ)のメモリを使用します。

一方、以下のブロックダイアグラムの場合を考えます。

上記と同様、入力は3つの関数に分岐します。ただし、この場合は「指標配列」関数は入力配列を修正しません。データを複数の場所に渡し、受け取り先がすべてデータを修正せずに読み取る場合、LabVIEWはデータのコピーを作成しません。結果として、すべてのデータはInPlaceとなります。このブロックダイアグラムは、約4KBのメモリを使用します。

最後に、以下のブロックダイアグラムを検討します。

この場合、入力は2つの関数に分岐し、そのうち1つの関数はデータを修正します。2つの関数は互いに依存しません。したがって、「部分配列置換」関数で安全にデータを修正するために、データのコピーが少なくとも1つ作成されることが予想されます。ただし、この例の場合、コンパイラはデータを読み取る関数を最初に実行し、データを修正する関数が最後に実行されるような実行します。つまり、「部分配列置換」関数は配列の複製を生成せずに入力配列バッファを再利用するため、この関数のデータはInPlaceとなります。ノードの順序が重要な場合は、シーケンスを使用するか、入力に別のノードの出力を使用することによって、順序を明示的に指定します。

実際には、コンパイラによるブロックダイアグラムの解析は正確でない場合があります。場合によっては、コンパイラがブロックダイアグラムのメモリの再利用の最適な方法を決定できないこともあります。

条件付きの表示器とデータバッファ

ブロックダイアグラムの構築方法を工夫することでデータバッファの再利用を防ぐことができます。条件付き表示器をサブVIで使用すると、LabVIEWがデータバッファの使用率を最適化できなくなります。条件付き表示器は、ケースストラクチャまたはForループの内側にある表示器です。条件付きで実行されるコードパスに表示器を配置するとシステムでデータフローが中断され、LabVIEWは入力からデータバッファを再利用しませんが、代わりにデータを表示器に強制的にコピーします。ケースストラクチャおよびForループの外側に表示器を配置すると、LabVIEWはループまたはストラクチャの内側にあるデータを直接修正し、データのコピーを作成する代わりにデータを表示器に渡します。ケースストラクチャの内側に表示器を配置する代わりに、別のケースの定数を作成することができます。

メモリ使用を監視する

メモリ使用を決定するには複数の方法があります。

現在のVIのメモリ使用量を表示するには、ファイル→VIプロパティを選択し、プルダウンメニューからメモリ使用を選択します。情報にサブVIのメモリの使用量が含まれていないことに注意してください。パフォーマンスおよびメモリをプロファイルウィンドウを使用して、メモリ上のVIにより使用されているメモリを監視し、パフォーマンスに問題があるサブVIを特定することができます。VIパフォーマンスおよびメモリをプロファイルウィンドウは一回のVI実行に使用されるバイトとブロック数の最小値、最大値、平均値の統計を維持します。

メモ   正確なVIの実行パフォーマンスを監視するには、次の理由を考慮してLabVIEWの現在のバージョンでコンパイルコードを分離することなくすべてのVIを保存します。
  • 取り消し機能を使用すると、一時的にオブジェクトとデータのコピーを作成し、そのため報告されるVIのメモリ要件を増加させる可能性があります。VIを保存すると取り消しにより生成されたコピーがパージされるため、正確なメモリ情報が報告されます。
  • LabVIEWの前バージョンで未保存のVIを実行すると、報告されるVIのメモリ要件を増加させ、VIの実行パフォーマンスを低下させる可能性があります。

メモリ領域がバッファとして割り当てられるブロックダイアグラムの部分を識別するには、バッファ割り当てを表示ウィンドウを使用します。

メモ  バッファ割り当てを表示ウィンドウは、LabVIEW開発システムおよびLabVIEWプロフェッショナル開発システムでのみ使用できます。

バッファ割り当てを表示ウィンドウを開くには、ツール→プロファイル→バッファ割り当てを表示を選択します。バッファを確認するデータタイプの横にチェックマークを付けて、更新ボタンをクリックします。ブロックダイアグラムに、データに領域を割り当てるためのバッファが、黒い四角形として表示されます。

各バッファに割り当てられるメモリの容量は、最大データタイプのサイズと同じです。これらのバッファは、VIの実行時にデータ保存に使用される場合とされない場合があります。データのコピーが作成されるかどうかは実行時に決定されるため、またVIがダイナミックデータに依存する可能性があるため、予測することは不可能です。

VIがバッファの割り当てにメモリを必要とする場合は、バッファのデータコピーが作成されます。バッファにデータコピーが必要かどうかが不明な場合は、コピーが作成される可能性があります。

メモ  バッファ割り当てを表示ウィンドウでは、ブロックダイアグラムに表示された黒い四角形によって実行バッファだけが表示されます。このウィンドウではフロントパネルの操作バッファは特定できません。

LabVIEWがバッファを作成する場所がわかれば、VIを編集してLabVIEWによるVIの実行に必要なメモリ容量を削減できる可能性があります。LabVIEWでは、バッファのすべてが除去されないように、VIを実行するためのメモリが割り当てられる必要があります。

LabVIEWによるVIの再コンパイルが必要となるような変更をVIに加えると、バッファ情報が正確でなくなるため黒い四角形の記号が消えます。バッファ割り当てを表示ウィンドウの更新ボタンをクリックして、VIを再コンパイルし、黒い四角形の記号を表示します。バッファ割り当てを表示ウィンドウを閉じると、黒い四角形の記号は消えます。

メモ  LabVIEW Desktop Execution Trace(デスクトップ実行トレース)ツールキットを使用して、スレッド使用率、メモリリーク、LabVIEWプログラミングに関するその他の点を監視することができます。

ヘルプ→バージョン情報を選択して、アプリケーションにより使用されているメモリ量の合計の統計および概要を参照します。これには、アプリケーションにより使用されているメモリと同様にVIのメモリについての情報が含まれます。VIのセットの実行前および実行後にこのメモリ量を確認し、VIにより使用されているメモリの量の概算値を取得することができます。

メモリをより効率的に使用するためのヒント

前のセクションでは、コンパイラはメモリを効率的に再利用することに重点が置かれました。コンパイラがメモリを再利用できるかできないかを判断する基準は複雑です。以下の基準を使用すると、メモリを効率的に使用するVIを作成するのに役に立ちます。

  • 通常、VIをサブVIに分割することで、メモリの使用量に悪影響はありません。多くの場合、サブVIが実行中でない場合、実行システムはサブVIのデータメモリを再度要求できるため、メモリの使用量は改善します。
  • メモリの使用量に悪影響を与えるには多大な量のスカラ値が必要となるため、スカラに関する問題はありません。
  • 配列または文字列を操作中にグローバル変数およびローカル変数を過度に使用しないでください。グローバル変数やローカル変数を読み取ると、LabVIEWがデータのコピーを生成する原因となります。
  • 必要でない場合は、開いているフロントパネルでサイズの大きい配列および文字列を表示しないでください。開いているフロントパネルの制御器と表示器は表示するデータのコピーを保持します。
ヒント  チャート表示器が必要な場合、チャート履歴は表示されているデータのコピーを保持することに留意してください。これはチャート履歴が一杯になるまで続き、一杯になるとそれ以上のメモリの使用を停止します。VIを再開始してもチャート履歴は自動的にクリアされないため、プログラムを実行することにより、このチャート履歴をクリアする方法が便利です。このプログラムの実行を行うには、空の配列にチャートの履歴データ属性ノードを書き込みます。
  • パネルアップデートを延期プロパティを使用します。このプロパティをTRUEに設定する際、制御器の値を変更したとしても、フロントパネルの表示器の値は変更されません。オペレーティングシステムは、表示器に新規値を入力するためにメモリを使用する必要はありません。
メモ  通常、LabVIEWはサブVIを呼び出す際にサブVIのフロントパネルを開きません。
  • サブVIのフロントパネルが表示されない場合、サブVIに未使用のプロパティノードを配置したままにしないでください。プロパティノードがあると、サブVIのフロントパネルがメモリに残り、不要なメモリが使用される原因となり得ます。
  • ブロックダイアグラムを設計する際、入力のサイズが出力のサイズと異なる場所に注意してください。たとえば、「配列連結追加」または「文字列連結」関数を使用して頻繁に配列または文字列のサイズが増加されている場所がある場合、データのコピーが生成されていることを意味します。
  • 配列には一致するデータタイプを使用して、データをサブVIおよび関数に渡す際に強制ドットを監視します。データタイプを変更する際、実行システムはデータのコピーを作成します。
  • クラスタや大きい配列または文字列を含むクラスタ配列など、複雑で階層化されたデータタイプは使用しないでください。より大量なメモリが使用される原因となることがあります。可能な場合、より効率的なデータタイプを使用します。
  • 必要でない場合は、透明で重複しているフロントパネルのオブジェクトは使用しないでください。このようなオブジェクトはメモリを大量に使用します。

VIのパフォーマンスを最適化する方法の詳細については、「LabVIEWスタイルチェックリスト」を参照してください。

フロントパネルのメモリ問題

フロントパネルが開いている場合、制御器と表示器は表示されているデータのプライベートコピーを保持します。

以下のブロックダイアグラムは、フロントパネルの制御器と表示器を含む「インクリメント」関数を示します。

VIを実行する際、フロントパネルの制御器のデータはブロックダイアグラムに渡されます。「インクリメント」関数は、入力バッファを再利用します。そして、表示器は表示の目的でデータのコピーを作成します。それゆえ、3つのバッファのコピーがあります。

フロントパネルの制御器のデータ保護をこの方法で行うと、データを制御器に入力し、関連VIを実行して、次のノードに渡される際に制御器におけるデータの変更を表示することを防ぎます。同様に、データは表示器の場合に保護されるため、新規データを受信するまで前の内容を確実に表示することができます。

サブVIを使用すると、制御器と表示器を入力と出力として使用することができます。実行システムは、以下の条件の場合にサブVIの制御器と表示器のデータのコピーを作成します。

  • フロントパネルがメモリにある。これは、以下のいずれかの理由で起こります。
    • フロントパネルが開いている。
    • VIが変更されたが、保存されていない(VIが保存されるまで、VIのすべてのコンポーネントがメモリに残ります)。
  • パネルがデータ印刷を使用している。
  • ブロックダイアグラムがプロパティノードを使用している。
  • VIがローカル変数を使用している。
  • パネルがデータロギングを使用している。

閉じられたパネルがあるサブVIで、プロパティノードがチャート履歴を読み取ることを可能にするには、制御器と表示器が渡されたデータを表示する必要があります。他にもこのような属性が多くあるため、実行システムはサブVIがプロパティノードを使用する場合にメモリにあるサブVIのパネルを保持します。

フロントパネルがフロントパネルのデータロギングまたはデータ印刷を使用する場合、制御器と表示器はデータのコピーを維持します。さらに、パネルはデータ印刷のためにメモリに保持され、パネルを印刷することが可能になります。

VIプロパティダイアログボックスまたはサブノード設定ダイアログボックスを使用して呼び出された場合に、フロントパネルを表示するようにサブVIを設定した場合は、サブVIを呼び出すとフロントパネルがメモリにロードされます。元々閉じていたら実行後に閉じる項目を設定した場合、LabVIEWはサブVIの実行が完了するとパネルをメモリから削除します。

サブVIによるデータメモリの再利用

通常、サブVIのブロックダイアグラムが最上位で複製されるように、サブVIは発呼者のデータバッファを簡単に使用することができます。ブロックダイアグラムの一部をサブVIに変換しても、メモリの使用量が増えることはほとんどありません。特別な表示条件を持つVIの場合、前述のように、フロントパネルと制御器に追加のメモリ使用量が必要な場合があります。

メモリが解放されるタイミング

以下のブロックダイアグラムを検討します。

平均」VIが実行された後、データの配列は必要なくなります。大きなブロックダイアグラムではデータがいつ必要なくなるか判断するのが困難なため、特定のVIのデータバッファの実行中にデータは解放されません。

macOSでは、実行システムのメモリが不足しているため、実行されていないVIによって使用されているデータバッファはすべて解放されます。実行システムは、フロントパネルの制御器、表示器、グローバル変数、または初期化されていないシフトレジスタによって使用されているメモリを解放しません。

ここで、同じVIを大きなVIのサブVIとして考えます。データの配列が使用され、サブVIのみで使用されます。macOSでは、サブVIが実行されず、システムのメモリが不足している場合、サブVIでデータが解放される場合があります。この場合、サブVIを使用することによって、メモリの使用量を節約することができます。

WindowsとLinuxのプラットフォームでは、VIが閉じられてメモリから削除されない限り、データバッファは通常解放されません。メモリは、必要に応じてオペレーティングシステムから解放され、仮想メモリがこれらのプラットフォームで活用されます。断片化により、アプリケーションが実際よりも多くのメモリを使用しているように見える場合があります。メモリが割り当てられて解放されると、アプリケーションはメモリ使用量を統合して、未使用のブロックをオペレーティングシステムに返すことができるようにします。

メモリ解放要求」関数を使用して、この関数を含むVIが実行された後に、未使用のメモリを解放することができます。この関数は、上級のパフォーマンス最適化のみに使用されます。場合によっては、未使用メモリを解放することでパフォーマンスを向上させることができます。ただし、頻繁にメモリを解放すると、LabVIEWがメモリの割り当てを再利用せずに、メモリ領域を繰り返し割り当てる原因となります。したがって、この関数は、使用するVIによって大量のデータが割り当てられるが、その割り当てが決して再利用されない場合にのみ使用してください。トップレベルVIがサブVIを呼び出すと、LabVIEWはそのサブVIが実行されるメモリのデータスペースを割り当てます。サブVIの実行が終了した場合、トップレベルVIの実行が終了するかアプリケーション全体が停止するまで、データスペースは解放されません。アプリケーション全体の停止は、メモリ不足の条件やパフォーマンスの低下を招くことがあります。メモリを解放するサブVIに「メモリ解放要求」関数を配置します。フラグブール入力をTRUEに設定すると、LabVIEWはサブVIのデータスペースを解放することによって、メモリの使用率を減少させます。

出力が入力バッファを再利用するタイミングを決定する

出力のサイズとデータタイプが入力と同じであり、入力が他で必要とされていない場合は、出力は入力バッファを再利用することができます。前述のように、入力が他の場所で使用されている場合でも、コンパイラと実行システムで入力を出力バッファに再利用するなどの方法でコードの実行を要求することができる場合もあります。ただし、この方法は規則が複雑なため、推奨されません。

バッファ割り当て表示ウィンドウを使用して、出力バッファが入力バッファを再利用するかどうかを確認することができます。以下のブロックダイアグラムでは、表示器をケースストラクチャの各ケースに配置すると、LabVIEWが各表示器のデータのコピーを作成するためデータのフローが中断されます。LabVIEWは入力配列に作成されたバッファは使用しませんが、出力配列のデータのコピーを代わりに作成します。

ケースストラクチャの外側に制御器を移動すると、出力バッファは入力バッファを再利用します。LabVIEWは制御器が表示するデータのコピーを作成する必要がないからです。LabVIEWは後でVIで入力配列の値を必要としないため、「インクリメント」関数は入力配列を直接修正して、出力配列に渡すことができます。この場合、LabVIEWはデータのコピーを必要としないため、以下のブロックダイアグラムに示されるようにバッファは出力配列に表示されません。

データタイプを一致させてメモリ使用量を最適化させる

一致するデータタイプを使用する場合、出力を生成するときに入力に割り当てられたメモリバッファを再利用することができます。これによりメモリの使用量とVIの実行時間が向上します。しかし、入力が出力と異なるデータタイプを持つ場合、出力はその入力に割り当てられたメモリを再利用できません。

たとえば、32ビットの整数を16ビットの整数に加算すると、16ビット整数は32ビット整数に強制変換されます。コンパイラは強制されたデータ用の新しいバッファを作成し、LabVIEWは「追加」関数上に強制ドットを配置します。この例では、入力がその他すべての要件を満たしていると推定して出力バッファ用に32ビットの整数を使用することができます。しかし、LabVIEWは16ビットの整数を強制変換するため、その入力に割り当てられたメモリを再利用することはできません。

メモリの使用量を最小化するには、可能な限り一致するデータタイプを使用してください。一致するデータタイプを使用すると、LabVIEWがデータを大きなサイズに強制変換する結果として少ない数のデータコピーが生成されます。一部のアプリケーションでは、データの使用量を最小に抑えるために小さなデータタイプの使用を考慮する必要があります。たとえば、8バイトの倍精度数値の代わりに、4バイトの単精度数値を使用することを検討してください。しかし、不要な強制変換を防ぐために、サブVIにワイヤするオブジェクトのデータタイプをサブVIが期待するデータタイプと一致させます。

特定タイプのデータを生成する際にメモリ使用量を最適化する

特定のデータタイプのデータを生成する必要がある場合、ブロックダイアグラム上のデータタイプを変換する必要があります。しかし、LabVIEWが大きな配列を作成する前に、変換関数を使用してデータタイプを変換することでメモリの使用量を最適化することができます。

次の例では、出力が別のVIの入力データアタイプと一致するよう単精度の浮動小数点データタイプである必要があります。LabVIEWは1,000のランダム値の配列を作成してスカラ値へその配列を追加します。LabVIEWは単精度の浮動小数点スカラデータタイプをランダム倍精度浮動小数点数に一致するよう強制するため、「追加」関数上に強制ドットを配置します。

次のブロックダイアグラムは、「単精度浮動小数に変換」関数を使用して倍精度浮動小数点数の配列を変換してこの問題を修正する試みを示しています。

関数はLabVIEWが配列を作成した後で大きな配列のデータタイプを変換するため、VIは強制ドットのある例と同じ量のメモリを使用します。

次のブロックダイアグラムは、LabVIEWが配列を作成する前に乱数を変換することでメモリの使用量と実行速度を最適化する方法を示しています。

変換を避けることができない場合、LabVIEWが大きな配列を作成する前に「変換」関数を使用してその配列に割り当てられている大きなデータバッファを1つのデータタイプから別のタイプに変換することを防止します。大きな配列が作成される前にデータを変換することで、VIのメモリ使用を最適化します。

データのサイズを常時変更することを避ける

出力サイズが入力サイズと異なる場合、出力は入力データバッファを再利用しません。「配列連結追加」、「連結文字列」、「部分配列」などの場合に、配列または文字列のサイズが増加したり減少したりするためこのようなことが起こります。配列と文字列を使用する場合は、これらの関数の常時使用を避けてください。データが常時コピーされるため、プログラムがより多くのメモリを使用し、実行速度が遅くなるためです。

サンプル1: 配列を作成する

データ配列の作成に使用される以下のブロックダイアグラムを調べます。このブロックダイアグラムは、「配列連結追加」を常に呼び出して新しい要素を連結することによって、ループに配列を作成します。入力配列は、「配列連結追加」によって再利用されます。VIは反復が実行されるたびにバッファのサイズを連続して変更して、新規配列にスペースを作成し、新しい要素を追加します。その結果、実行速度は遅くなり、特にそのループが何度も実行される場合はその影響が顕著になります。

メモ  配列、クラスタ、波形、バリアントを操作する場合、In Place要素ストラクチャを使用するとVIでのメモリ使用を向上できます。

ループが繰り返されるたびに配列に値を追加する場合は、ループの端にある自動指標付けを使用することによって、最適なパフォーマンスを実現することができます。Forループは、VIは配列のサイズを事前に判断することができ(Nに配列される値に基づいて)、バッファのサイズを一回のみ変更することができます。

Whileループの場合は、配列の終点のサイズがわからないため、自動指標付けは効率的ではありません。ただし、Whileループの自動指標付けは、出力配列のサイズを大幅増分で増やすことによって、各反復で出力配列のサイズが変更されるのを防ぎます。ループが完了すると、出力配列は正しいサイズに変更されます。Whileループの自動指標付けのパフォーマンスは、Forループと自動指標付けとほぼ同等です。

自動指標付けは、ループの反復が実行されるたびに結果配列に値を追加することが前提となります。一定の条件の場合に値を追加する必要があり、配列サイズの上限を指定できる場合は、配列を事前に割り当てて、「部分配列置換」を使用して配列を入力することを考慮してください。

配列値を入力したら、配列を正しいサイズに変更することができます。配列は一度のみ作成され、「部分配列置換」は出力バッファに入力バッファを再利用できます。パフォーマンスは、自動指標付けを使用ループの場合のパフォーマンスとよく似ています。この方法を使用する場合、「部分配列置換」は自動的に配列のサイズを変更しないため、値を変更する配列が結果データを十分に保持できる大きさであることに注意してください。

このプロセスの例は、以下のブロックダイアグラムに示されています。

サンプル2: 文字列を検索する

パターンで一致」関数を使用して、パターンに一致する文字列を検索できます。検索の仕方によっては、不要な文字列データバッファを作成することになり、パフォーマンス速度が低下することがあります。

文字列で整数を一致させたいことを仮定した場合、[0–9]+をこの関数への正規表現の入力として使用できます。文字列で整数の配列を作成するには、ループを使用して、返されるオフセット値が–1になるまで「正規表現を一致」を繰り返し呼び出します。

以下のブロックダイアグラムは、文字列内のすべての整数の発生をスキャンする1つの方法です。空の配列を作成し、ループの各反復で残りの文字列で数値パターンを検索します。パターンが検出されると(オフセットは–1でない)、このブロックダイアグラムは「配列連結追加」を使用して、数値を結果配列数に追加します。文字列に値が残されていない場合、「正規表現を一致」は–1を返し、ブロックダイアグラムの実行が終了します。

このブロックダイアグラムの問題の1つは、「配列連結追加」をループで使用して、新規値を以前の値に連結することです。代わりに、自動指標付けを使用して、ループの端に値を累算できます。「正規表現を一致」によって一致が検出されず、最後のループの反復によって生成された必要のない値が配列にあることに注意してください。これは、「部分配列」を使用して、必要のない値を削除することで解決できます。これは、以下のブロックダイアグラムで示されています。

このブロックダイアグラムのもう1つの問題は、ループを通じて必要のない文字列のコピーが毎回作成されることです。「正規表現を一致」には、検索を開始する場所を示すために使用できる入力があります。前回の反復のオフセットを覚えている場合は、この数値を使用して次の反復で検索を開始する場所を示すことができます。この方法は、以下のブロックダイアグラムで示されています。

効率的なデータ構造を開発する

前述の例では、サイズが大きな配列または文字列を含むクラスタまたは配列などの階層データ構造は、効率的に処理できないということを説明しました。このセクションでは、その理由とより効率的なデータタイプを選択する方法を説明します。

複雑なデータ構造の問題は、生成の目的でアクセスする要素のコピーを作成せずに、データ構造内で要素にアクセスまたは変更するのが難しいことがあげられます。これらの要素が大きい場合に要素自体が配列または文字列の場合、これらの余分なコピーはメモリを使用し、メモリをコピーするのに時間がかかります。

通常、スカラデータタイプは効率的に処理できます。同様に、要素がスカラの小さい文字列と配列は効率的に処理することができます。スカラの配列の場合、以下のコードが配列で値を増分する方法を示しています。

メモ  配列、クラスタ、波形、バリアントを操作する場合、In Place要素ストラクチャを使用するとVIでのメモリ使用を向上できます。多くのLabVIEWの操作では、データ値をコピーしてメモリ内に保持する必要があるため、実行速度が下がり、メモリ使用量が増加します。In Place要素ストラクチャは、メモリ内でデータ値の複数コピーを作成することなく、一般的なLabVIEWの操作を実行します。その代わり、In Place要素ストラクチャは同じメモリ位置にあるデータ要素で操作が行われ、それらの要素は配列、クラスタ、バリアント、または波形内の同じ位置に返されます。データ要素はメモリ内の同じ位置に返されるため、LabVIEWのコンパイラはメモリ内に余分なデータコピーを作成する必要がありません。

この方法は、配列全体の余分なコピーを生成する必要がないため、より効率的です。また、「指標配列」関数によって生成された要素はスカラで、効率的に作成して処理できます。

クラスタがスカラのみを含む場合、クラスタの配列の場合も同様です。以下のブロックダイアグラムでは、「バンドル解除」または「バンドル」を使用する必要があるため、要素の処理はより複雑になります。ただし、クラスタはおそらく小さいため(スカラのメモリ使用量は少ない)、クラスタ要素にアクセスして要素を元のクラスタに戻す操作に関連する重要なオーバーヘッドは発生しません。

以下のブロックダイアグラムは、バンドル解除、操作、再バンドルの効率的なパターンを示します。データソースからのワイヤは、「バンドル解除」関数の入力と「バンドル」関数の中央の端子の2つだけの配線先があるはずです。LabVIEWはこのパターンを認識し、パフォーマンスがより優れたコードを生成することができます。

各クラスタに大きいサイズの部分配列または文字列が含まれているクラスタの配列を使用する場合、クラスタでの指標付けと要素値の変換操作はメモリと時間がかかりる場合があります。

配列全体で要素を指標付けすると、その要素のコピーが作成されます。それゆえ、クラスタのコピーと、対応する大きな部分配列と文字列が作成されます。文字列と配列のサイズは変数であるため、コピー処理にはオーバーヘッドが実行に文字列や部分配列のデータをコピーする他に、メモリの割り当てによる適切なサイズの文字列や部分配列の作成の呼び出しが必要になります。これは、数回のみの実行する場合は、重要ではありません。ただし、使用しているアプリケーションがこのデータ構造に頻繁にアクセスする場合、メモリと実行のオーバーヘッドも使用例によっては考慮する必要があります。

データの表示方法を変更して、解決することもできます。以下の3つの例は、3つの異なるアプリケーションと、それぞれの最適なデータ構造を示しています。

ケーススタディ1: 複雑なデータタイプを回避する

いくつかのテストの結果を記録するアプリケーションを検討します。結果に、テストを説明する文字列とテストの結果の配列が必要です。この情報を保存する上で、以下のフロントパネルで示されているデータタイプを使用することを検討してください。

配列で要素を変更するには、配列全体で要素を指標付けする必要があります。ここで、配列に到達するには、クラスタには要素をバンドル解除する必要があります。配列の要素を置換して、結果の配列をクラスタに保存します。最後に、結果のクラスタを元の配列に保存します。この例は、以下のブロックダイアグラムに示されています。

バンドル解除または指標付けの各レベルで、データのコピーが生成される場合があります。コピーは必ずしも生成される必要がないことに注意してください。データのコピーには、時間とメモリが浪費されます。データ構造をできるだけ平坦にすることが解決策になります。たとえば、この例ではデータ構造を2つの配列に分割しました。最初の配列が文字列の配列で、2番目の配列が2D配列で、各行は実行されたテストの結果です。この結果は、以下のフロントパネルで示されています。

このデータストラクチャの場合、以下のブロックダイアグラムに示されるように、「部分配列置換」関数を使用して配列要素を直接置換することができます。

ケーススタディ2: 混合データタイプのグローバルテーブル

情報のテーブルを保持する必要があるアプリケーションの別の例です。このアプリケーションでは、データのグローバルアクセスが必要であるとします。このテーブルには、ゲイン、電圧の上限と下限、チャンネルの参照に使用された名前など、計測器の設定が含まれています。

アプリケーション全体でデータへのアクセスを可能にするには、以下のChange Channel Info VIとRemove Channel Info VIなどのサブVIのセットを作成して、テーブルでデータにアクセスする方法があります。

これらのVIは、以下の3通りの方法で実装できます。

基本的な実装

この関数のセットでは、基本テーブルにいくつかのデータ構造を検討することができます。まず、各クラスタにゲイン、下限、上限、チャンネル名が含まれたクラスタの配列を含むグローバル変数を使用することができます。

前のセクションで説明したように、このデータ構造はデータにアクセスするのに数レベルの指標付けとバンドル解除が必要がため、効率的に処理するのが容易ではありません。また、データ構造はさまざまな情報の集合体であるため、「1D配列検索」関数を使用してチャンネルを検索できません。「1D配列検索」を使用してクラスの配列で特定のクラスタを検索することはできますが、単一のクラスタ要素に一致する要素を検索することはできません。

代替方法1

上記の例と同様、データを2つの異なる配列に格納します。1つ目の配列にはチャンネル名、もう一方にはチャンネルデータが含まれます。名前の配列の各チャンネル名の指標は、もう1つの配列の対応するチャンネルデータを検索するために使用されます。

文字列の配列がデータと異なるため、「1D配列検索」を使用してチャンネルを検索することができます。

Change Channel Info VIを使用して1000個のチャンネルの配列を作成する場合、この実装方法を使用すると基本の実装方法と比較して約2倍高速化されます。ただし、他にもパフォーマンスに影響を与えるオーバーヘッドが存在するため、この変更はそれほど大きな変化をもたらしません。

グローバル変数から読み取る場合、グローバル変数のデータのコピーが生成されます。そのため、配列のデータのコピーが要素にアクセスするたびに生成されます。次は、このオーバーヘッドを回避するより効率的な方法です。

代替方法2

グローバルデータを格納する別の方法は、初期化されていないシフトレジスタを使用することです。基本的に、初期値を配線しない場合、シフトレジスタは呼び出し間でその値を記憶します。

LabVIEWのコンパイラはシフトレジスタへのアクセスを効率的に処理します。シフトレジスタ値を読み取ることによって、データのコピーが必ずしも生成されるわけではありません。実際には、シフトレジストに格納された配列を指標付けして、配列全体の余分なコピーを生成せずに値を変更したり、更新することもできます。シフトレジスタの問題は、シフトレジスタを含むVIのみがシフトレジスタのデータにアクセスできることです。一方、シフトレジスタはモジュール式である利点があります。

チャンネルの読み取り、変更、削除、またはすべてのチャンネルのデータを削除するように指定するモード入力を持つ単一のサブVIを作成することができます。

サブVIには、2つのシフトレジスタ(チャンネルデータとチャンネル名の1つ)があるWhileループが含まれます。これらのシフトレジスタのいずれも初期化されません。次に、Whileループの内側にモード入力に接続されたケースストラクチャを配置します。モードの値によtte、シフトレジスタの値を読み取り、そのデータを変更する必要がある場合もあります。

以下は、これらの3つの異なるモードを処理するインタフェースがあるサブVIの概要です。Change Channel Infoコードのみが表示されています。

1000の要素の場合、この方法では前回の実装の2倍、最初の実装の4倍の速度になります。

ケーススタディ3: 文字列のスタティックグローバルテーブル

前述の例では、アプリケーションに複数のデータタイプを含むテーブルがあり、そのテーブルが頻繁に変更される場合について説明しました。多くのアプリケーションでは、いったん作成された後は情報のテーブルは比較的に固定的になります。テーブルは、スプレッドシートファイルから読み取られる場合もあります。一度メモリに読み取られた後は、主に情報の検索に使用します。

この場合、「ファイルから表を初期化」および「表から記録を取得」の2つの関数が実行される場合があります。

テーブルを実行する1つの方法として、文字列の2次元配列を使用できます。コンパイラが、文字列の配列の各文字列を異なるブロックメモリに格納することに注意してください。多数の文字列がある場合(例: 5000個以上の文字列)、メモリマネージャにロードを配置してください。このロードが、各オブジェクトの数が増えるにつれて、パフォーマンスの低下を引き起こす場合があります。

サイズの大きいテーブルを保存する別の方法として、単一の文字列として表を読み取ることができます。次に、文字列の各レコードのオフセットを含む別の配列を作成します。これにより構成が変更され、比較的にサイズの小さい数千のメモリブロックの代わりに、単一のサイズの大きいメモリブロック(文字列)と別のサイズの小さいメモリブロック(オフセットの配列)が作成されます。

この方法を実行するにはより複雑な操作を要しますが、サイズの大きいテーブルではより速い速度を実現することができます。



この記事は役に立ちましたか。

役に立たなかった