FMXでTStringGrid処理が遅い

解決


yTake  2017-03-14 19:40:27  No: 48585

今回はFMXでのTStringGridの使い方についてご教授願えるでしょうか?
実数の二次元配列をStringGridへ表示させます。
配列の大きさは毎回変わるので可変とします。
var
  StringColumn_2  : Array of TStringColumn;

begin

  SetLength( stringColumn_2, col2 );
  StringGrid2.RowCount  :=  row2;
  time2 :=  Time();
  for i := 0 to col2 - 1 do
  begin
    stringcolumn_2[ i ] :=  TStringColumn.Create( StringGrid2 );
    StringGrid2.AddObject( stringcolumn_2[ i ] );
    stringcolumn_2[ i ].Width :=  36;
    stringcolumn_2[ i ].Header  :=  IntToStr( i );
    for j := 0 to row2 - 1 do
      StringGrid2.Cells[ i, j ] := FloatToStrF( MTX[ i, j ].dens, ffFixed, 5, 0 );
  end;
  time2 :=  Time() - time2;

end

としてみました。
配列サイズが約500x200の時、約1分強ほど要しました。
配列サイズが約1500x550の時、約10分以上要しました。
デバッガで追いかけてみましたが、配列サイズが大きいと後半になれなるほど処理が重たくなっている印象です。

データの配列表示は正しくされています。ただ、スクロール動作が重く、スクロールは出来ません。スクロールバー上をクリックで数秒後そこまで飛ぶ感じです。

何かTStringGrid操作上で改善点・改良点をご教授頂ければと思います。
高速に処理する方法などあるのでしょうか?
そもそもTStringGridはサイズが1000を超える様な大きな配列の表示には向かないのでしょうか?

TGridなども検討しています。
取り扱うデータ型によってColumnタイプを使い分ける様ですが、実数型のColumnタイプは無さそうです。
あれば、”FloatToStr”の型変換しなくて済むので、少しは早くなるかな?とも思いました。
ただ、こちらはStringColumnタイプを使うとしても、未だ、コードが出来上がっていません。TStringGridとも多少使い方が違う様です。

TStringGridを使うとして、配列処理が早くなる手段はあるでしょうか?
また、表示後の動作の遅さについては、対策があるのでしょうか?

よろしくお願い致します。

当方、
Windows10+DELPHI XE6


igy  2017-03-14 20:48:05  No: 48586

外しているかもしれませんが・・・

FMXのTStringGridは、文字列のデータを保持しますが、
TGridの場合、GetValueメソッド・SetValueメソッドで、
今ある配列からデータを取得・格納する処理になりそうなので、
もしかしたら、処理が早い可能性が・・・


take  2017-03-15 00:45:36  No: 48587

記述した表示処理は何のイベントで処理されているのでしょうか?

TStringGridのザイズが大きくなってもクリックやスクロールで
遅くなる要素は無いかと思います。


yTake  2017-03-15 04:11:50  No: 48588

igyさん、takeさん、ありがとうございます。

igyさん、
ダメ元でTGridで実現すべくトライしています。Gridへのアクセス方法が違う為、多少、苦労しております。実現出来たら、結果をお伝えします。

takeさん、
事象は二つあります。
先ず、配列データをStringGridへ表示させる場合、これはボタンイベント(onClick)中の処理です。この中で、ファイルから配列データを読み込み、StringGridへ表示しています。(コードは前述)
次に、スクロール時の場合、これは私は何も記述していません。
上記、ボタンイベントが終了し、Form画面に配列が表示されたStringGridが表示され、そのスクロールバーをクリックしました。この場合、何ら、イベント処理は記述していません。StringGridのデフォルトの機能と思います。

”TStringGridのザイズが大きくなってもクリックやスクロールで遅くなる要素は無い”との事なので、デバッガが要因かもと思い、”デバッガを用いずに実行”を試してみましたが、結果は変わりません(実はより少し遅くなった)

引き続き、よろしくお願いします。


igy  2017-03-15 04:36:06  No: 48589

行:1000
列:500
で、試したところ、StringGrid, Gridともに、

> 表示後の動作の遅さ

は、ありませんでした。

ただ、列の数が多ければ、列の追加に時間がかかるみたいですね・・
列:200のとき3秒なのが、列:500では21秒ぐらいかかりました。

Windows 7 + Delphi 10.1 Berlin


igy  2017-03-15 04:40:16  No: 48590

あと、

> TGridの場合、・・・ 
> もしかしたら、処理が早い可能性が・・・ 

と書きましたが、そんなに変わりませんでした・・・


igy  2017-03-15 04:43:26  No: 48591

あと、

> DELPHI XE6

と、少し古いのが、気になったのですが、

私がテストしたのは、以下のコードですが、これ動かしたら、表示後の動作は、どうなりますか?

const
    TEST_ROW = 1000;
    TEST_COL = 200;

procedure TForm1.Button1Click(Sender: TObject);
var
    sCol: TStringColumn;
    c, r: Integer;
    t: TDateTime;
begin
    t := Time();
    for c := 1 to TEST_COL do
    begin
        sCol := TStringColumn.Create(StringGrid1);
        sCol.Name := 'col' + IntToStr(c);
        sCol.Header := 'テスト' + IntToStr(c);
        sCol.Width := 60;
        StringGrid1.AddObject(sCol);
    end;

    with StringGrid1 do
    begin
        RowCount := TEST_ROW;

        for r := 0 to RowCount - 1 do
            for c := 0 to ColumnCount - 1 do
                Cells[c, r] := Format('[%d, %d]', [c, r]);
    end;
    Label1.Text := TimeToStr(Time() - t);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
    sCol: TStringColumn;
    c: Integer;
    t: TDateTime;
begin
    t := Time();
    for c := 1 to TEST_COL do
    begin
        sCol := TStringColumn.Create(Grid1);
        sCol.Name := 'col' + IntToStr(c);
        sCol.Header := 'テスト' + IntToStr(c);
        sCol.Width := 60;
        Grid1.AddObject(sCol);
    end;
    Grid1.RowCount := TEST_ROW;

    Label1.Text := TimeToStr(Time() - t);
end;

procedure TForm1.Grid1GetValue(Sender: TObject; const ACol, ARow: Integer;
  var Value: TValue);
begin
    Value := Format('[%d, %d]', [ACol, ARow]);
end;


yTake  2017-03-15 06:21:53  No: 48592

igyさん、ありがとうございます。

TGridの使い方、参考になります。

ご提示のコードを試してみました。

Col=200で同じく3秒くらいだったものが、Col=500で、StringGridが28秒、Gridで23秒でした。

また、表示後の動作は、
スクロールバーの矢印クリック:数秒してから表示が移動
スクロールバーの場所クリック:数秒してから表示が飛ぶ(飛んだ位置はほぼ正しい)
スクロールバーのカーソルドラッグ:全く動きません。
でした。

スクロールバーのカーソルドラッグでは、リアルタイムに表示が変わって欲しいのですが、無理な相談なのでしょうか?

XE6が遅いというより、PCが遅いのでしょうか?本機はIntelMacBookProでBootcampでWindows10を起動しています。これまで、これほどまでの遅さを感じたことはありません。
明日、別のWindowsPCでも試してみます。
なお、配列データの読出しStringGrid/Gridへの書き込みは、igyさんの所要時間とほぼ同様の傾向を示していました。

なんとか解消したいところです。


igy  2017-03-15 08:18:22  No: 48593

先のテスト結果は、

>列:200のとき3秒なのが、列:500では21秒ぐらいかかりました。 
>
>Windows 7 + Delphi 10.1 Berlin 

でしたが、別の遅いPC(Celeron N2807)の Windows 10 に
・Delphi 10.1 Berlin Update 2 (Starter Edition)
・Delphi XE8 (Starter Edition) # Update 1 未適用
がありますので、これでも試してました。

・Delphi 10.1 Berlin Update 2 (Starter Edition)
  列:200・・・10秒
  列:500・・・59秒

  <列:200でテスト>
  スクロールバーの矢印クリック:すぐスクロール開始。
  スクロールバーのカーソルドラッグ:リアルタイムに表示が変わる。 

・Delphi XE8 (Starter Edition)
  列:200・・・42秒
  列:500・・・4分28秒

  <列:200でテスト>
  スクロールバーの矢印クリック:スクロール開始。
                                # 10.1 Berlinと比べ、若干、開始まで時間がかかっている気も・・
                                # とはいえ、1秒以内には、動作している。
  スクロールバーのカーソルドラッグ:リアルタイムには表示が変わらないが、ドロップ時に表示が変わる。 

もし、yTakeさんも、Delphi 10.1 Berlin Starter Edition をお持ちでしたら(今でも無償で入手できます)
試してみるのも、よいかと思います。


au  2017-03-15 18:12:56  No: 48594

列の追加処理してる部分を
BeginUpdate/EndUpdate ではさめば速度の改善見られませんか?


igy  2017-03-15 19:03:51  No: 48595

auさん、情報ありがとうございます。

>列の追加処理してる部分を 
>BeginUpdate/EndUpdate ではさめば速度の改善見られませんか? 

これを試してところ、

・Delphi 10.1 Berlin Update 2 (Professional Edition) # Windows 7
  列:200・・・ 3秒 → 1秒以下
  列:500・・・21秒 → 4秒

・Delphi 10.1 Berlin Update 2 (Starter Edition) # Celeron N2807, Windows 10
  列:200・・・10秒 →  1秒
  列:500・・・59秒 → 10秒

・Delphi XE8 (Starter Edition) # Celeron N2807, Windows 10
  列:200・・・42秒    →  6秒
  列:500・・・4分28秒 → 28秒

のように改善されました。


take  2017-03-15 19:18:18  No: 48596

TStringColumnはわかりませんが
TStringGridにはBeginUpdateはあるものの速度は変わらなかったかと思います。

描画が遅いのであればVisibleをFalseにすれば良いのですが
スクロール処理でそんなに時間がかかるというのはちょっと聞いたことがありません

参考サイト
http://mam-mam.net/mytech/show.php?cd=85


yTake  2017-03-15 20:36:58  No: 48597

auさん、igyさん、takeさん、ありがとうございます。

おかげ様まで、処理が格段に速くなりました。
BeginUpdate/EndUupdate
を挿入してみました。挿入箇所は以下の通りです。

procedure TForm2.Button1Click(Sender: TObject);
var
    sCol: TStringColumn;
    c, r: Integer;
    t: TDateTime;
begin
    t := Time();

    StringGrid1.BeginUpdate();
    for c := 1 to TEST_COL do
    begin
        sCol := TStringColumn.Create(StringGrid1);
        sCol.Name := 'col' + IntToStr(c);
        sCol.Header := 'テスト' + IntToStr(c);
        sCol.Width := 60;
        StringGrid1.AddObject(sCol);
    end;

    with StringGrid1 do
    begin
        RowCount := TEST_ROW;

        for r := 0 to RowCount - 1 do
            for c := 0 to ColumnCount - 1 do
                Cells[c, r] := Format('[%d, %d]', [c, r]);
    end;
    StringGrid1.EndUpdate();

    Label1.Text := TimeToStr(Time() - t);
end;

procedure TForm2.Button2Click(Sender: TObject);
var
    sCol: TStringColumn;
    c: Integer;
    t: TDateTime;
begin
    t := Time();
    Grid1.BeginUpdate();
    for c := 1 to TEST_COL do
    begin
        sCol := TStringColumn.Create(Grid1);
        sCol.Name := 'col' + IntToStr(c);
        sCol.Header := 'テスト' + IntToStr(c);
        sCol.Width := 60;
        Grid1.AddObject(sCol);
    end;
    Grid1.RowCount := TEST_ROW;
    Grid1.EndUpdate();

    Label2.Text := TimeToStr(Time() - t);
end;

TStringGridのBeginUpdate/EndUupdateは、よく分からなかったので、参考サイトにある
  StringGrid1.Cols[ i ].BeginUpdate;
  StringGrid1.Cols[ i ].EndUpdate;
ではなくて、
StringGrid1自体に続けました。

結果、
    TEST_ROW = 550;
    TEST_COL = 1550;
の場合で、
TStringGridの方が、13秒
TGridの方で、6秒
でした。

次に表示のスクロールですが、こちらは少しタイムラグがあり、
スクロールバーの矢印クリック:1〜2秒後に移動
スクロールバーの場所クリック:1〜2秒後に移動
スクロールバーのカーソルドラッグ:クリックを放してから1〜2秒後に移動
と言う感じです。

ただ、これらは全て、XE6での結果です。

”DELPHI Berlin 10.1 Starter Edition”をインストールして同じ試験を行いました。

uses節に、”FMX.Grid.Style”の追加を促された以外は問題なく実行されました。

結果は、
TStringGridの方が、1分4秒
TGridの方で、1分2秒
でした。
一方で、スクロールは早いです。
スクロールバーの矢印クリック:クリック直後に移動
スクロールバーの場所クリック:クリック直後に移動
スクロールバーのカーソルドラッグ:配列サイズの影響もあると思いますが、
                Row方向はリアルタイムで移動、Col方向は若干遅れますが、
                ドラッグしたカーソルに追随します

Colも追加も早くて、スクロールも早いと言う分けにはなかなかいきません。

XE6でスクロールに時間がかかるのは何故でしょう?
プログラムしていない箇所なので、手の出しようがないですよね。

なお、上記試験は全て、
DELL Precision T1650, Intel Core i3 3.3GHz, RAM:24GB
64bit Windows 10 Pro
での結果です。


igy  2017-03-15 21:57:22  No: 48598

>    TEST_ROW = 550; 
>    TEST_COL = 1550; 
>の場合で、 

この条件で試してみました。

OS:Windows 7
CPU:Celeron G1630
メモリ:8GB
Delphi 10.1 Berlin Update 2 (Professional Edition)

結果は、
TStringGrid・・1分17秒 
TGrid・・・・・1分17秒 

時間、かかりますねぇ・・・

コードは、以下のとおり。

const
    TEST_ROW = 550;
    TEST_COL = 1550;

procedure TForm1.Button1Click(Sender: TObject);
var
    sCol: TStringColumn;
    c, r: Integer;
    t: TDateTime;
begin
    t := Time();

    with StringGrid1 do
    begin
        BeginUpdate;
        for c := 1 to TEST_COL do
        begin
            sCol := TStringColumn.Create(StringGrid1);
            sCol.Name := 'col' + IntToStr(c);
            sCol.Header := 'テスト' + IntToStr(c);
            sCol.Width := 60;
            AddObject(sCol);
        end;
        EndUpdate;

        RowCount := TEST_ROW;

        for r := 0 to RowCount - 1 do
            for c := 0 to ColumnCount - 1 do
                Cells[c, r] := Format('[%d, %d]', [c, r]);
    end;
    Label1.Text := TimeToStr(Time() - t);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
    sCol: TStringColumn;
    c: Integer;
    t: TDateTime;
begin
    t := Time();

    with Grid1 do
    begin
        BeginUpdate;
        for c := 1 to TEST_COL do
        begin
            sCol := TStringColumn.Create(Grid1);
            sCol.Name := 'col' + IntToStr(c);
            sCol.Header := 'テスト' + IntToStr(c);
            sCol.Width := 60;
            AddObject(sCol);
        end;
        EndUpdate;

        RowCount := TEST_ROW;
    end;

    Label1.Text := TimeToStr(Time() - t);
end;

procedure TForm1.Grid1GetValue(Sender: TObject; const ACol, ARow: Integer;
  var Value: TValue);
begin
    Value := Format('[%d, %d]', [ACol, ARow]);
end;

で、

> XE6でスクロールに時間がかかるのは何故でしょう? 

おそらくバグだと思いますが、

> プログラムしていない箇所なので、手の出しようがないですよね。 

update 1を適用されていない場合、適用してみるのもよいかと思いますが、

RAD Studio XE6(Delphi XE6/C++Builder XE6) Update 1 における不具合修正リスト 
http://edn.embarcadero.com/article/43925

を見ても、該当する部分の修正の記載がないみたいですし、

Update Subscription向けの

ID: 30430, October 2015 XE6 Update Subscription Update
http://cc.embarcadero.com/item/30430

でも、修正の記載がないので・・・


yTake  2017-03-15 23:29:15  No: 48599

igyさん、ありがとうございます。

XE6のバージョンを確認しました。update1は適用済みです。

”ID: 30430, October 2015 XE6 Update Subscription Update”の所はよくわかりません。

いずれにしましても、XE6ではこれ以上の改善は見込めない様ですね。

DELPHI 10.1 Berlinの方のColumn追加をより早くできたらと思います。
XE6並みにと行きたいところですが、せめて今の半分くらいに出来たら良いです。
取り敢えず、VisibleをFalseにしてみましたが、ほとんど変わりませんでした。

ソースによりますと、igyさんはColumn追加の部分のみBeginUpdate/EndUupdateされていますが、私はStringGrid処理全体をBeginUpdate/EndUupdateしてみましたが、おおきな相違はみられません。

また、TStringGridとTGridとでは、私の方では、igyさんの推察通り、TGridの方が僅かですが、早い結果が出ています。

これ以上は無理そうでしょうね。


yTake  2017-03-16 02:40:19  No: 48600

yTakeです。

一応、一通りの解決を見たので、閉めさせて頂きます。

皆様、大変ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加