2010年9月25日土曜日

[.NET]いまさら?Parallel.Forを使ってみた(その1)

簡単に平行処理ができる、Parallel.Forというクラスが.NET4で提供されています。で、以前作成した、GDD Blog: [.NET]画像処理グレースケール化(1)の処理をParallel.Forにしたら速くなるか?とおもってちょっと実験。小さい画像だと差が出ずらいので、ちょっと大きめの画像(3,008×2,000)で上記リンクの処理を5回動作させてみると、平均で16.32秒でした。タスクマネージャでCPUの稼動を見ているとほぼ50%で停滞。というか1コアしか使っていない。

で、以下のコードで実行してみると、CPU稼動率は100%で張り付いたものの、5回の平均を計ると18.297秒。なんと10%くらい性能が悪化していました。BitmapのGetPixおよびSetPixはマルチスレッドに対応していないようで、結果的に同期のコストがかかることが原因ということがわかりました。

で、残念なコードは以下のとおり。
private void btnGlay2_Click(object sender, EventArgs e)
{
    textBox1.Text = "グレースケール化(2)";

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

    Bitmap bmp = new Bitmap(ctlPicture.Image);
    int h = bmp.Height;
    int w = bmp.Width;
    Parallel.For(0, w, delegate(int i)
    {
        for (int j = 0; j < h; j++)
        {
            Color color;
            lock (bmp)
            {
                color = bmp.GetPixel(i, j);
                // 計算式にて、値を算出してセットする
                //int col = (int)(color.R * 0.299 + color.G * 0.587 + color.B * 0.114);
                //int col = (int)((color.R + color.G + color.B) / 3);
                int col = (int)Math.Round((color.R + color.G + color.B) / 3.0d);
                Color pix = Color.FromArgb(col, col, col);
                bmp.SetPixel(i, j, pix);
            }
        }
    });
    ctlPicture.Image = bmp;

    sw.Stop();
    textBox1.Text = String.Format(textBox1.Text + " 完了{0:#,##0}ms", sw.ElapsedMilliseconds);
}

2010年9月17日金曜日

[他]VBSで指定時間待つダイアログ

うちの会社の勤怠システムは、URLのGETで打刻を実施しています。
毎日ブラウザでGETするのでもいいですが、面倒なので、VBSでGETするやつを組んでみました。
土曜日とかは打刻したくないときもあるので、確認のダイアログを出して打刻するかどうかを確認する仕様です。

が、コレを押すのを忘れちゃうことが時々あって。。。ということで、一定時間経過したら自動的にダイアログを自動的に閉じて処理を継続する方法を見つけたので紹介します。朝、おなかが痛くても、ログインさえすれば、打刻OK的な。。。でコードはこんな感じ。

Dim objShell: Set objShell = WScript.CreateObject("WScript.Shell")
'第2引数:待ち時間
'第4引数:出力アイコン+ボタンパターン
Dim intRes: intRes = objShell.Popup("やっちゃいますか?",3 ,"どうする?",32+1)

Select Case intRes
   case vbOk     MsgBox("OK!!")
   case vbCancel MsgBox("Cancel!!")
   case -1       MsgBox("Time Out!!")
End Select

2010年9月15日水曜日

[他]VBSでHTTP通信&文字のエンコード

えぇ、まぁうちの会社の打刻システムはHTTPでGETすることにより打刻します。

先日システムのバージョンアップがあり、知らぬ間にShiftJISからMS932にエンコードへ変えられていました。
MSXML2.ServerXmlHttpではMS932には対応していないため、取得した文字列が正しくデコードされず、日本語が化けてしまいます。で、標準的なもので何とかならないものか。と調査してみると、ADODB.Streamで解決できることがわかりました。ということでこんな感じ(一部手打ちなので、エラーが出たらスマソ)。

Dim objHttp: Set objHttp = WScript.CreateObject("MSXML2.ServerXmlHttp")
Dim strXML

Call objHttp.Open("GET""https://hogehoge.com/kintai/dakoku.do?id="&strID, False)
If objHttp.status = 200 Then
    Dim objADO:    Set objADO = WScript.CreateObject("ADODB.Stream")

    objADO.Open
    objADO.Type = 1'adTypeBinary
    objADO.Write objHttp.responseBody

    objADO.Position = 0
    objADO.Type = 2'adTypeText
    objADO.Charset = "Shift_JIS"
    strXML = objADO.ReadText()
Else
    MsgBox("HTTP ERROR !! CODE=" & objHttp.status)
End If

2010年9月9日木曜日

[.NET]byte.Equals(int)

先日、バイト列の中を検索し、特定のコードが出てくる位置を特定するという処理を組んでいました(共有ファイルのやつね)。コード的には、Array.IndexOfをつかって0x00を探しているのですが、なぜか結果が-1(みつからない)で、もしかして型の違いというのを思い出して、実験してみました。

int.Equals(byte)はtrueなのに、byte.Equals(int)はfalseなんですね。バイト列の検索には要注意ですね。でコードはこんな感じ。

private void button1_Click(object sender, EventArgs e)
{
    var buf = new byte[16] { 0123456789101112131415 };

    var idx = 0;
    //がっかりなコード
    //for (idx = 0; idx < buf.Length; idx++)
    //{
    //    if (buf[idx] == 9)
    //    {
    //        break;
    //    }
    //}

    //残念なコード(-1がかえる)
    //idx = Array.IndexOf(buf, 9);

    //普通のコード
    idx = Array.IndexOf(buf, (byte)9);

    System.Diagnostics.Debug.WriteLine("発見位置=" + idx);

    int int1 = 1;
    byte byte1 = 1;

    System.Diagnostics.Debug.WriteLine("int1==byte1? ->" + (int1 == byte1));
    //int1==byte1? ->Trueと表示される
    System.Diagnostics.Debug.WriteLine("byte1.Equals(int1)? ->" + byte1.Equals(int1));
    //byte1.Equals(int1)? ->False
}