SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

.NETでマンデルブロ集合を描く(後日談)
――Task Parallel Library/Parallel Patterns Libraryの利用

マルチスレッドでもっと高速化


前準備:Bitmapの排除

 そんなわけで高速化の戦略は「計算コビト(内の計算ルーチン)を増員し、各コビトを並行動作させる」こと。シングルコア機ではマルチスレッドにしたところで一つのスレッドしか動けないので高速化は望めませんが、マルチコア機なら少しは速くなってくれることでしょう。幸いにも絵描きコビトは数百msごとの描画(Bitmapから画面への転写)が主な仕事であり、それが終わればつぎの描画まではほぼ休止状態のはず。そうであれば複数の計算コビトが働く余地があるでしょうから。

 では前準備、方眼紙:SectionPaperから排他アクセスが必要なためにコビトたちの並行動作を妨げるBitmapを抜き取り、代わりに要素数が方眼紙の幅×高さであるint配列を持たせ、計算コビトは計算結果を方眼紙内のint配列に書き込むことにします:

List-01:Bitmapを排除した方眼紙:SectionPaper(C#)
public class SectionPaper {

  private int[,] bitmap_;
  private double xa_, xb_;
  private double ya_, yb_;
  private int width_, height_;

  public SectionPaper(int width, int height) {
    width_ = width; height_ = height;
    bitmap_ = new int[width_, height_];
  }

  public void SetScale(DComplex ul, DComplex br) {
    xa_ = (br.Real() - ul.Real()) / width_;
    xb_ = ul.Real();
    ya_ = (br.Imag() - ul.Imag()) / height_;
    yb_ = ul.Imag();
  }

  public int GetWidth() {
    return width_;
  }

  public int GetHeight() {
    return height_;
  }

  public DComplex GetPoint(int x, int y) {
    return new DComplex(x*xa_+xb_, y*ya_+yb_);
  }

  public void SetPixel(int x, int y, int c) {
    bitmap_[x,y] = c;
  }

  public int GetPixel(int x, int y) {
    return bitmap_[x, y];
  }

  public void Clear() {
    for (int x = 0; x < width_; ++x ) {
      for (int y = 0; y < height_; ++y ) {
        bitmap_[x, y] = -1;
      }
    }
  }

}

 方眼紙はもはや描画機能を持たなくなったので、描画コビト(MainForm)で面倒みなければなりません:

List-02:MainFormの描画部(C#)
public partial class MainForm : Form {

  private Bitmap bitmap_; // 追加
  private SectionPaper paper_;
  private ColorPalette palette_;

  public MainForm() {
    InitializeComponent();
    bitmap_ = new Bitmap(pbxResult.Width, pbxResult.Height); // 追加
    paper_ = new SectionPaper(pbxResult.Width, pbxResult.Height);
    palette_ = new ColorPalette();
  }

  // SectionPaperからBtmapへの転写
  private void Draw() {
    int w = paper_.GetWidth();
    int h = paper_.GetHeight();
    lock ( bitmap_ ) {
      for ( int x = 0; x < w; ++x ) {
        for ( int y = 0; y < h; ++y ) {
          // SectionPaper上の各点の値を色に変換してBitmapへ
          bitmap_.SetPixel(x, y, palette_.GetColor(paper_.GetPixel(x, y)));
        }
      }
    }
  }

  …………

  // 定期的な描画
  private void tmrDraw_Tick(object sender, EventArgs e) {
    Draw();
    Invalidate();
  }

  // MainFormのPaintイベント・ハンドラ
  private void MainForm_Paint(object sender, PaintEventArgs e) {
    lock ( bitmap_ ) {
      using ( Graphics g = pbxResult.CreateGraphics() ) {
        // BitmapをPictureBoxに描画
        g.DrawImage(bitmap_, g.VisibleClipBounds);
      }
    }
  }
}

 ……実はこれだけの変更で見違えるほどに速くなりました。1600[ms]だったのが800[ms]足らず、2倍以上のスピードアップです。計算コビトは描画コビトとは(BakgroundWorkerが用意してくれた)別のスレッドで動いていますから、両者間の排他制御が必要なくなったために二人のコビトがお互いの邪魔をせずに動いてくれたのでしょう。

次のページ
TPLによる並行動作

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/4697 2009/12/28 16:27

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー