何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 720
  • 記事 - 18
  • コメント - 2684
  • トラックバック - 217
ニュース
  • IE7以前では、表示がおかしい。div の解釈に問題があるようだ。
    IE8の場合は、「互換」表示を OFF にしてください。
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2010
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

2010年8月31日

最近に始まったことではないが、落ち着きがない。前エントリーの原因にしろ、昨日の妻からのメールにしろ、落ち着いて読んだら間違って解釈してるとわかろうものを、ススッと読んで、パパッと処理して間違っている。

今のところ、仕事ではやってなさそうなんだけど、点検し直した方がよさそう。

「そんな時間…」と言いたいが、後戻りの方が、時間食うからなぁorz

posted @ 20:45 | Feedback (0)
 

昨日か今日あたり、私から郵便が送られた人がいらっしゃると思います。

申し訳ありませんが、半分を送り返して下さい送信もと住所を書かず、切手無しで送れば、受取人支払いになるので、それでお願いします。

お手間をとらせて申し訳ありませんが、お願いします。

中さんにも、申し訳ない。

posted @ 20:38 | Feedback (0)

2010年8月5日

Open MP は、C++ ではライブラリとプリプロセッサ指示によって使用する。プリプロセッサ指示は、「ディレクティブ」と、ディレクティブでの動作に細かな指示を出す「句」(富士通さんの日本語訳リファレンスでは、「指示節」と訳されている)に分かれる。

ディレクティブは、ディレクティブを置いた次の文に対してのみ有効となる。その為、curly bracket ('{', '}')でブロックを作る(ことが必要になる)場合が多い。


parallel ディレクティブが現れるまでは、シングル スレッドで動作する。う~ん?parallel ディレクティブが有効なブロックを抜けた後は、どうなる?…タスクマネージャが報告する「スレッド数」は、複数のまま。Win-API の WaitForSingleObject, WaitForMultiObject をするとスレッドは消える(終了する)が、Open MP のは終了しないらしい。が、次の parallel ディレクティブで、指定されているスレッドの数が異なると、その数にあわせて終了される、または生成される。おっと、Process Explorer で見ると、スレッドの数が変わるときは、マスター スレッド以外は全て作り直しているようだ(スレッド ID が変わる、変わらないで判断)。同じ時は、そのまま流用している。parallel ディレクティブで num_threads 句を使うより、omp_set_num_threads 関数を使って制御する方が良さそうだ。

parallel ディレクティブで作成されるスレッド数は、次の順番で決まる。

  1. num_threads 句が指定されていれば、その数。
  2. omp_set_num_threads 関数が実行されていれば、直前の実行で指定された数。
  3. 環境変数 OMP_NUM_THREADS が定義されていれば、その数。
  4. いずれでも決まらなければ、実装依存(VC++ の場合は、論理 CPU の数)。

なんだけど、omp_set_dynamic 関数、あるいは環境変数 OMP_DYNAMIC で指定する、スレッド数の自動調整機能の影響を受けるようだ。そして、MSDN ライブラリの説明は、TRUE/FALSE の説明が逆転しているような気がする。おいおい、関数と環境変数の優先順位も、なんだか矛盾していないか?

3.1.7 omp_set_dynamic 関数(OpenMP Reference)より:

dynamic_threads が 0 以外の値に評価される場合、以降の並列領域の実行に使用されるスレッド数は、システム リソースが最適に利用されるよう、ランタイム環境によって自動的に調整されます。結果として、開発者が指定するスレッド数が最大スレッド数になります。

え~っと、自動調整が有効になるから、開発者が指定するものではなく、システム リソースによって最大スレッド数が決まるんじゃないか?実際、omp_set_dynamic(0); をコールして、num_threads(10) とすると、10個のスレッドが上がる。omp_set_dynamic(1); をコールして、num_threads(10) としても、2個のスレッドしか上がらない。デフォルトがどっちかは、実装依存らしい。VC++ の場合は、FALSE。

3.1.7 omp_set_dynamic 関数(OpenMP Reference)より:

omp_set_dynamic の呼び出しは、OMP_DYNAMIC 環境変数より優先されます。

4.3 OMP_DYNAMIC(OpenMP Reference)より:

ただし、omp_set_dynamic ライブラリ ルーチンが呼び出されて、この動的調整機能が明示的に有効または無効に設定されている場合、こちらの方が優先されます。

環境変数 OMP_DYNAMIC の方の、「こちらの方が優先されます」という書き方がおかしいのか。「こちら」が、環境変数を指しているのか、関数を指しているのか、わかりにくい。「omp_set_dynamic ライブラリ ルーチンの方が優先されます。」なんだろうな、きっと。OpenMP の仕様書に、スレッド数を決める実際のアルゴリズムがあってそこに出てくるけど、他のことがわかってないと読みにくいのでまた今度。


ところで。私は明示的にスレッドを扱うプログラムは、1度しか書いたことがない。OpenMP も、当時からあったみたいだけれど、最近まで知らなかった。知らないことを付け焼き刃な知識で書くと、失敗する。なので、再勉強中。え?遅い?勘弁してくださいorz

posted @ 22:41 | Feedback (0)

2010年7月27日

「ひょっとして、これって、スレッド作るのがとっても簡単になるんじゃないの?」と思ったので、遊び始めた。

まず、OpenMP を使わずに、Windows API だけでスレッドを作ってみる。


// http://msdn.microsoft.com/en-us/library/ms682516.aspx より
// #include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <strsafe.h>
#define MAX_THREADS    10
#define BUF_SIZE 255
typedef struct {
    int    x;
    int    y;
} ThreadArgument, *PThreadArgument;
DWORD WINAPI threadFunction(LPVOID lpParam)
{
    HANDLE hStdout;
    PThreadArgument pData;
    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hStdout == INVALID_HANDLE_VALUE) { return 1; }
    // 引数をキャストする
    pData = (PThreadArgument) lpParam;
    // スレッド セーフな関数を使って表示する。
    StringCchPrintf(msgBuf, BUF_SIZE,
        TEXT("パラメータ = %d, %d\n"), pData->x, pData->y);
    StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
    WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
    return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwThreadId[MAX_THREADS];      // スレッド ID
    HANDLE hThread[MAX_THREADS];        // スレッド ハンドル
    ThreadArgument datas[MAX_THREADS];
    for (int i = 0; i < MAX_THREADS; ++i) {
        dwThreadId[i] = 0;
        hThread[i] = NULL;
    }
    for (int i = 0; i < MAX_THREADS; ++i) {
        datas[i].x = i * 100;
        datas[i].y = i + 100;
        // 生成
        hThread[i] = CreateThread( NULL, 0,
            threadFunction, &datas[i],
            0, &dwThreadId[i]);
        if (hThread[i] == NULL) {
            for (int j = 0; j < MAX_THREADS; ++j) {
                if (hThread[j] != NULL) { CloseHandle(hThread[j]); }
            }
            ExitProcess(i + 100);
        }
    }
    WaitForMultipleObjects(MAX_THREADS, hThread, TRUE, INFINITE);
    for (int i = 0; i < MAX_THREADS; ++i) {
        if (hThread[i] != NULL) { CloseHandle(hThread[i]); }
    }
    return 0;
}

OpenMP を使って、同等の「結果を得られる」コードを書くと、こうなる。


// #include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <strsafe.h>
#define MAX_THREADS    10
#define BUF_SIZE 255
typedef struct {
    int    x;
    int    y;
} ThreadArgument, *PThreadArgument;
DWORD WINAPI threadFunction(ThreadArgument arg)
{
    HANDLE hStdout;
    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hStdout == INVALID_HANDLE_VALUE) { return 1; }
    // スレッド セーフな関数を使って表示する。
    StringCchPrintf(msgBuf, BUF_SIZE,
        TEXT("パラメータ = %d, %d\n"), arg.x, arg.y);
    StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
    WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
    return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
    #pragma omp parallel for num_threads(MAX_THREADS)
    for (int i = 0; i < MAX_THREADS; ++i) {
        ThreadArgument arg;
        arg.x = i * 100;
        arg.y = i + 100;
        threadFunction(arg);
    }
    return 0;
}

threadFunction 関数は同じなので、_tmain 関数がとても短くなっている。

さて、「同じ結果」は得られるが、「同等の処理」ではない。

まず、「#pragma omp parallel」によって、スレッドが作られる。Win-API の方は、for ループの中でスレッドを作っているが、OpenMP の方はループの外で作っていることになる。また、「#pragma omp for」によって、事前に for 文が解析される。事前解析で何回ループするかを判断する。今回はコンパイル時にわかるが、実行時にしかわからないこともある。その時でも、ループを始める前に何回回るかを解析するのは一緒。ループの回数を、作成してあるスレッドの数に、このコードでは等分に分配する。Win-API のコードでは、必ず MAX_THREADS 回のループが回るが、OpenMP では(このコードでは)「ループの回数/スレッドの数」の回数しかループしていない。

さて、「事前に for 文が解析される。事前解析で何回ループするかを判断する。」と書いた。よって、ループ中にループ回数を変化させるようなことをしてはいけない。while 文や do-while 文を使うことは出来ないし、for ブロックから抜ける構文があってもいけない。ループの回数が判定できないような終了条件を書くこともできない。おそらく、for ブロック内で終了条件を変化させるようなことをしてもいけないと思う。これらの多くは、コンパイル時に検出される。

すなわち、for ディレクティブによる並列化は、「同じことを何度もするんでしょ?じゃぁ、複数の人数でやりましょうよ。そうすると、一人一人の仕事量が減り、仕事を完了する時間が早くなるでしょ。」と言うことだ。

が、スレッドを使ってやりたいのは、そんなことなのだろうか?確かにそれもひとつだが、「なぁ、これやっといてぇな。俺、こっちせなあかんねん。」というような事もある。例えば、Java や .NET Framework のランタイムは、ガーベッジコレクションを別のスレッドで動作させている。この様な、スレッド毎に別々の仕事をさせたいときは、どうすればいいのだろう?(続く予定)

posted @ 22:43 | Feedback (1)

2010年7月23日

前作から10年後という設定。

今回の敵は、前作のシャイゲンにいた生物学者の弟子が作ったという、モンスター。

前作からの違い。

最初に選べる車が少い。武器を持って帰っても、必ずストックされるわけではない。何時でも参加、中断可能な4人同時プレー。より強力な武器。移動スキルが5で使用可能な飛行スーツ。アホになって車の前に飛び出してくる一般人。シナリオ、エージェントが3つまで保存できる。

協力プレーは強力です。ホストぬきで勝手にシナリオを進められます。ろくに強くならないうちに進められ、エンディングを迎えてしまいます。

そのため、シナリオ用の3つのエントリーを使います。1つは協力プレー用。1つは独りでじっくりプレー用。今回はliveオーブという、協力者がいないと取得できないオーブがあるので、他のプレイヤーの所へ乱入だ!



そして3に続く...

posted @ 18:25 | Feedback (0)

2010年7月22日

今年度から、組み込み Linux で仕事をすることになりました。と思ったら、先月中から Windows アプリケーション作成のヘルパーに突っ込まれました。いや、「作りながら仕様が決まっていく」ってのは、精神衛生上、良くないです。私にとっては。

そんな中、メインの仕事で、「標準の memcpy は遅いから、(32ビット CPU なので)4バイトずつ転送するように作り直して」と、指示されました。

え?まさか?標準のライブラリの方が、私なんかが作るより速いでしょ?

とか思いながら、実機ではデバッグ環境がないので、Visual Studio 上でコードを書き、テストして、計ってみます。やっぱり、標準の方が速い。それを実機用にコンパイルし直し、実機で計ると…

書き直した方が速い!

何でやねん?

Microsoft による実装を見ました。アセンブリが読めません。代わりにコメントを読むと、SSE2 が使える環境では、SSE2 を使います。そうでなければ、転送先が4の倍数なら4バイトずつ、そうでなければ1バイトずつ転送する。となっていました。

次。Linux も見ます。アーキテクチャによる memcpy が定義されていないなら、1バイトずつ転送するようになっていました。じゃ、アーキテクチャによる実装は?PowerPC のアセンブリは、さっぱりわかりません。しかし、IBM のエンジニアが書いているらしいので、それくらいの工夫はされているように思います。

ということは、開発環境が参照するライブラリがヘボいってこと?まさかなぁ?

posted @ 18:11 | Feedback (6)

2010年6月23日

最初から全部出来る人なんて、いないよね。

25年前。初めてパソコンに触れた頃。『マイコン BASIC マガジン』という雑誌がありました。同年代で、お世話になった人は多いはず。あるゲームだったともうけど、この様な絵が描かれていました。

・・・悲しいなぁ。PC-6001 の SCREEN 4 でこれを表示すると、色がにじんでとっても奇麗だったのよ。

で、この部分だけ取り出して、表示させるわけですわ。まぁ、次のようなコードになります。


// フォームに、ToolStripBar を貼り付け、ボタンを1つ追加しました。
// PictureBox を貼り付け、DockStyle を、Fill にしました。
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1() {
            InitializeComponent();
        }
        Bitmap bmp;
        private void toolStripButton1_Click(object sender, EventArgs e) {
            if (bmp != null) {
                bmp.Dispose();
            }
            bmp = new Bitmap(pictureBox1.Size.Width,
                pictureBox1.Size.Height,
                System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            Graphics g = Graphics.FromImage(bmp);
            try {
                for (int x = 0; x < bmp.Width; x += 5) {
                    g.DrawLine(Pens.Blue,
                        new Point(0, 0),
                        new Point(x, bmp.Height));
                }
                for (int y = 0; y < bmp.Width; y += 5) {
                    g.DrawLine(Pens.Blue,
                        new Point(0, 0),
                        new Point(bmp.Width, y));
                }
                pictureBox1.Image = bmp;
            } finally {
                g.Dispose();
            }
        }
    }
}

プロジェクトファイルはここ

で、ここから、一歩ずつ、発展させていくんですね。

  1. ラインを、右上、右下から描くようにするには?

  2. 線の間隔(増分値)を、プログラムを変更することなく変更出来るようにするには?

  3. 線を、中心から引くようにするには?

  4. [button1]をクリックしなくても描画するためには?(フォームのサイズを変更すると、[button1]をクリックしないと再描画しません。これを、クリックしなくても再描画するようにするには?)

  5. 中心から、円形に線を引くには?

  6. 増分値を三角関数にするとどうなる?

  7. PC-6001 は、テレビに出力していました。テレビは、RGB の画素が並んでいるので、1ドットの線を引くと、赤と緑、または赤と青の線になりました(3色のはずなのに2色なのは、ブラウン管の構造による)。これを再現するには?

    縦に1ドットの線を引くと、赤になります。その隣に1ドットの線を引くと、緑(または青)になります。2ドット以上の幅のある線を引くと、白になります。

挑戦者求む!!出題のアレンジも!!ま、6番までは簡単だわね。7番は、赤と緑(または青)のストライプにすることは出来るけど、2ドット以上をどうやって白にしてやろう?

posted @ 22:05 | Feedback (4)