三重ループでデータの動的配列への取り出しを行なっています。
それに時間が掛かっていて高速化を図りたいのですが、遅い理由が分っていません。
三重ループの直前と直後で、タイムスタンプ(now)を取ってみました。
Word型で300x300x300個のデータを取り出すのに2~8秒程度掛かります。
データは元々ファイルに保存されていてそれを一括でメモリーへ読み出しています。このファイルからの読み出しは瞬時に完了しています。(1秒未満)
メモリー上に読み出したデータへはポインターでアクセスし、三重ループで配列要素へ逐次代入しています。ここで時間が掛かっています。(自己版とします)
但し、代入時に乗算を施しまた、最大値も併せて抽出しています。
更に、抽出した最大値を用いて、配列要素の正規化を行ないまた255階調化を行なっています。
つまり、三重ループを2回廻している事になります。
それでも、vramさん提示のサンプルを改変して同様の処理を試したところ、瞬時(1秒未満)に完了しています。(PWordArrayの場合と同等である印象)
なお、元のファイルは配列データのみならず、様々な型のデータが混在しています。また、動的配列へ取り込むデータもWord型の場合と稀にSingle型の場合があります。
従いまして、受け皿を構造体配列にしています。例えば、100個の構造体要素の75番目に、このWord型三次元配列を取り込みとします。
受けは、
type RMTX_Data = Record
data2 : Word;
data3 : Single;
data4 : Single;
data5 : Byte;
End;
type RMTX = Record
X : Word;
Y : Word;
Z : Word;
MTX : Array of Array of Array of RMTX_Data;
ptr2 : PWord;
ptr4 : PSingle;
ENd;
type TMTX = Class
MTX : Array of RMTX;
procedure get_MTX(idx:Byte);
procedure cp_MTX(idx:Byte);
End;
を定義して、get_MTX内でファイルから一括で配列データをメモリーへ読み出し、cp_MTXでメモリーから動的配列への代入と演算を行なっています。
基本構造はサンプル版と自己版とでほぼ同じです。
然しながら、三重ループの直前・直後でタイムスタンプを取ると、サンプル版と自己版と実行速度が大幅に違っています。
ループ内の処理も同じな為、原因が分かりません。
for k := 0 to Z - 1 do
for j := 0 to Y - 1 do
for i := 0 to X - 1 do
begin
MTX[idx].MTX[i,j,k].data2 := ptr2^;
MTX[idx].MTX[i,j,k].data3 := ptr2^ * val;
if (i=0) and (j=0) and (k=0) then
max := MTX[idx].MTX[i,j,k].data3
else
if max < MTX[idx].MTX[i,j,k].data3 then
max := MTX[idx].MTX[i,j,k].data3;
inc(ptr2);
end;
このループに2秒ほど、
for k := 0 to Z - 1 do
for j := 0 to Y - 1 do
for i := 0 to X - 1 do
begin
MTX[idx].MTX[i,j,k].data4 := MTX[idx].MTX[i,j,k].data3 / max;
MTX[idx].MTX[i,j,k].data5 := Trunc( 255 * (1-MTX[idx].MTX[i,j,k].data4));
end;
このループは8秒くらい掛かっています。Truncを使っているからでしょうか?
ただ、サンプル版では1秒未満です。
ループ外部の影響と言う事でしょうか?
サンプル版ではFormは1つでコンポーネントもボタンが数個だけです。
一方、自己版は既にFormが多数存在していて、本件ループが存在するFormにもコンポーネントが多数載っています。
ただ、実行しているのは、本件のファイルからデータを読み込んで動的配列へ代入する事だけで、他のフォームを開いていたり、何か別の作業を実施している分けではありません。
ループ処理の高速化の為に、何をどう確認したり、対処すればよいでしょう?
ちょっと、行き詰まっている感じです。
すみません。
当方、Windows10+8GB, DELPHI XE6+FMXで行っています。
さすがにそれだけの差は謎な感じですね。
一つ確認してほしいのは構造体アライメントがデフォルトの8になっているかどうかですね。
メインメニュー→プロジェクト→オプションでプロジェクトオプションを開いて、Delphiコンパイラ→コンパイルにある、
コード生成のレコードフィールドのアライメントがクワッドワード(=8バイト)になっているか、またレコード型の定義にコンパイラ指令{$A ...}または{$ALIGN}の指定がないかどうか、確認してみてください。
サンプル版って?
for k := 0 to Z - 1 do
begin
for j := 0 to Y - 1 do
begin
for i := 0 to X - 1 do
begin
MTX[idx].MTX[i,j,k].data4 := MTX[idx].MTX[i,j,k].data3 / max;
MTX[idx].MTX[i,j,k].data5 := Trunc( 255 * (1-MTX[idx].MTX[i,j,k].data4));
end;
end;
end;
を
var
AAA: RMTX;
BBB: RMTX_DATA;
begin
AAA := MTX[idx];
for k := 0 to Z - 1 do
begin
for j := 0 to Y - 1 do
begin
for i := 0 to X - 1 do
begin
BBB := AAA.MTX[I,J,K];
BBB.data4 := BBB.data3 / max;
BBB.data5 := Trunc( 255 * (1-BBB.data4));
end;
end;
end;
HFUKUSHIさん、ありがとうございます。
”レコードフィールドのアライメント”は”クワッドワード”となっています。
また、ソースコード内のレコード定義に”{$A}”又は"{$ALIGN}"はありません。
全くの謎です。
AAAAAさん、ありがとうございます。
サンプル版とは、vramさん(AAAAAさん)に提供頂いたコードを元にPWordArrayで速度を確認させえて頂いた上で、ループ部分を自己版の様に改変し速度を比較したものです。
結果、非常に高速に実行される事が、分った次第です。(ありがとうございました)
今回のご助言は、演算を構造体の配列要素へ直接参照で行わず、一旦変数に代入して、廻すと言うものと思います。
所要時間が半分ほどにはなりました。
代入操作が増えていますが、演算に直接配列を参照しないと言う事でかなり高速化するものですね。
ただ、サンプル版と比べて、未だ、遅い様に思います。
これ以上の高速化を図れない事はないと思います。
何が影響しているか、見当もつきません。
何か、試すべき事は他にあるでしょうか?
>サンプル版とは、vramさん(AAAAAさん)に提供頂いたコードを元にPWordArrayで速度を確認させえて頂いた上で、ループ部分を自己版の様に改変し速度を比較したものです
ん??
>ただ、サンプル版と比べて、未だ、遅い様に思います。
サンプル版そのまま使えばって思うんだけど?
AAAAAさん、ありがとうございます。
原因の特定が困難な為、作り直す事にしました。
サンプル版に基づいて作り直しています。
このまま、他のフォームを組み込んで行けば、良さそうです。
また、ループが遅くなる様な事があれば、その時組み込んだものが何らかの影響を及ぼしていると推察されます。
お騒がせしました。
ありがとうございました。
ツイート | ![]() |