From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

NI製品ディスカッション

キャンセル
次の結果を表示 
次の代わりに検索 
もしかして: 

数値データタイプ小型化と変換によるFPGAリソース量の違いについて

FPGA VIの最適化の一つとして、数値データの軽量化を行いたいと考えています。

「小さいデータタイプを使用する」、「強制ドットを解消する」ことが必要と伺ったのですが、

入力とデフォルト値の違う場所はどのように扱った方が効率的か教えてください。

 

ループの初期化として0を8か所接続する必要がありました。しかし接続先のデフォルトタイプがバラバラです。この場合、

→初期化のデータタイプを1番大きいところに合わせて定数を一つ使用するのと、

→初期化のデータタイプは1番小さいU8として定数は一つとし、接続先に対応する変換をかますのと、

→それぞれのデータタイプと合わせた初期値0を使用するのではどの手法が1番リソース量が少ないですか。

 

基本的な質問で恐縮ですが、なにとぞご教授の方よろしくお願いいたします。

0 件の賞賛
メッセージ1/4
3,780件の閲覧回数

こんにちは、hide09さん。

 

直観的には「それぞれのデータタイプと合わせた初期値0を使用するのではどの手法」がもっともリソースが少なそうに見えますが結局コンパイラに依存すると思いますので、実際にやってみて試すのが一番いいかもしれません。

0 件の賞賛
メッセージ2/4
3,702件の閲覧回数

hide09 様

 

この質問は3つの選択肢のどれが良いのかは単純には言えないです。

各データタイプで処理する必要がある演算がどのくらいの量あるのかによって結果が違うからです。

 

処理の大半が一番大きいデータタイプで演算しなければ精度が保てない場合に、

ごく一部だけ他のタイプを残しても変換の処理も考慮すると、ほとんど節約にはなりません。

 

U8のままで大半の処理可能で、一部分だけ別のタイプが必要なら2番目の選択もあると思います。

 

最適な処理で軽量化を行う時の基本は、一つ一つの演算で最適な最小のサイズのデータタイプを使用する事です。

その点からすると、初期値に限った事では無く演算も含め、それぞれの処理で必要な最小のデータタイプを選択するのが基本です。

 

明確な回答では無くて分かりにくいですが、参考にして頂けたら幸いです。

0 件の賞賛
メッセージ3/4
3,668件の閲覧回数

解決策でなく余談ですみません。

ただ、この手の最適化は興味のあるところです。

 

mizutagain様やKONDOH様のおっしゃるように、コンパイラに依存するし、

どれが良いかは単純には言えないのは確かと思います。

 

FPGAとCPUではアーキテクチャが違うので「0をロードする」という操作のコンパイル結果も違いますが、

CPUの場合だと、特にx86系では、「0」のロードは実際にどこかメモリ上なりコード上に0という数値が書いてあって

それを持ってくるというよりは、レジスタ演算で0を作ってしまう方が簡単で速くリソースも食わないので、

以下はあくまでCPUの場合の予想ですが、「8個のゼロを、ループの初期化に食わせる」場合は、

一番サイズの大きい0(例えばI32)から分岐させると、内部的には、レジスタクリアの常套手段の

xor eax,eax命令eaxレジスタを0にし、32bitのところへはそのままeaxレジスタを食わせ、

16bitのところへはaxレジスタを食わせ(=eaxレジスタの下半分)、

8bitのところへはalレジスタを食わせる(=axレジスタの下半分)、ということが出来るので、

ゼロ値も32bitの0をどこかへ格納する必要もなく、ワイヤ上で型変換があったとしても

既にゼロのレジスタの一部分を使えばよく、全般的なリソースは一番効率的なように思います。

ただし、viが内部でそうコンパイルするかは分かりません。

 

一方、一番サイズの小さい0(例えばI8)から分岐させると、内部的には、最初にxor al,al命令alレジスタを0にし

(ただし、xor al,al命令よりxor ax,ax命令の方がコードが短いので、そっちにしてalだけ使うかも知れない)、

16bitのところへはcbw命令axレジスタに拡張し(U8→U16又はI8→I16に相当)、

32bitへはさらにcwde命令eaxレジスタに拡張し(U16→U32又はI16→I32に相当)、

必要なら64bitへはさらにcdq命令edx:eaxまで、もしくはcdqe命令で64bitのraxレジスタまで拡張して食わせる感じです。

この場合、いちいち拡張するコードが挟まるので、実行時間的リソースは非効率です。

ゼロ値はどこへも格納されずに記述できるので、最小サイズから始めるメリットは少ないかも知れません。

 

 

すべてバラバラに8個のゼロを書いた場合は、素直にコンパイルすれば、その都度たとえばxor ax,ax命令

などでゼロを作って食わせていく可能性はありますが、既にゼロなのにまた次のxor ax,ax命令など

ゼロにしようとしてる・・・とかいう点での無駄はあるかも知れません。

 

ただ、LabVIEWのコンパイラも、(期待の上では)それなりに賢い・・・と思うので、ハイライト実行でない限り

どちらで書いてあっても結局もっとも効率的な方法で「ここは全部ゼロが共通じゃん」と思ってかしこく

コンパイルしてくれちゃうのかなぁ、などと想像しますが、こればかりは分かりません。

(LabVIEWでdllを作って、逆アセンブルしてみればいいのかも知れませんが・・・・)

 

FPGAの場合はアーキテクチャも全然違うので上記は全く無関係ですが、

リソースで、1バイト2バイトを争う場合は、やはりそれぞれ試して比べてみるのが一番かもですね。

0 件の賞賛
メッセージ4/4
3,651件の閲覧回数