[ カテゴリー » PC関連 ]

PostgreSQLで遅くなるクエリ

CentOS 4.9Link &PostgreSQLLink (postgresql-server-8.1.11-1.el4s1.1)での話。

  • 200以上のカラムを持つテーブルが数10あるDBを構築
  • 各テーブルにはName varchar(10)とTime datatimeのカラムを含む
  • Nameに対してbtreeのインデックスが張ってある
  • 同じNameを持つ行が1~数行存在し、 NameとTimeの組み合わせでは重複は無い

こういうテーブルに対し以下のSQLを発行すると2~3秒かかる現象が。

SELECT max(Time),min(Time) FROM table1 WHERE Name='hoge'

つまりName='hoge'がどの時間範囲で存在しているかを知るのが目的。似たような構造を持つテーブルは他にもたくさんあるのに、現象が発生するテーブルは一つだけ。しかもデータ行が100万を越えた辺りから発生する。テーブルを一旦削除してデータを入れなおしても同じ。

一方以下のクエリは見た目一瞬で完了。

SELECT Name FROM table1 WHERE Name='hoge'
SELECT count(Name) FROM table1 WHERE Name='hoge'

最終的に以下のクエリが期待している速度で完了するようなので、理由はともかく採用。

SELECT max(Time),min(Time) FROM table1 WHERE Name='hoge' GROUP BY Name

Name自体はSELECTする項目ではなくWHERE句でもNameは単一として指定しているので、GROUP BYの有無に関わらず出る結果は同じはず。一方オプティマイザは両者では異なる方針を出すようで、GROUP BYを付けないとインデックスを2回使ったりTimeがNULLかどうかを調べたり。

db1=> explain SELECT max(Time),min(Time) FROM table1 WHERE Name='hoge' GROUP BY Name;
                                          QUERY PLAN                                          
----------------------------------------------------------------------------------------------
 GroupAggregate  (cost=0.00..2908.69 rows=1 width=22)
   ->  Index Scan using index1 on table1  (cost=0.00..2901.15 rows=1003 width=22)
         Index Cond: (Name = 'hoge'::bpchar)
(3 rows)

db1=> explain SELECT max(Time),min(Time) FROM table1 WHERE Name='hoge';
                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Result  (cost=526.88..526.89 rows=1 width=0)
   InitPlan
     ->  Limit  (cost=0.00..263.44 rows=1 width=8)
           ->  Index Scan Backward index1 on table1  (cost=0.00..264491.27 rows=1004 width=8)
                 Filter: ((Time IS NOT NULL) AND (Name = 'hoge'::bpchar))
     ->  Limit  (cost=0.00..263.44 rows=1 width=8)
           ->  Index Scan using index1 on table1  (cost=0.00..264491.27 rows=1004 width=8)
                 Filter: ((Time IS NOT NULL) AND (Name = 'hoge'::bpchar))
(8 rows)

う~む、よく分からん(- -;)

— posted by mu at 12:19 pm   commentComment [0]  pingTrackBack [0]

G-Tune NEXTGEAR Note i300到着

blog20120623-GTune_i300

もちろんPSO2Link を実家・出張先でやるため。坊や、運が悪かったね、こんな家に来たばっかりに遠い国(注:フィリピン)に連れて行かれてしまうんだよ…

早くから候補は軽さのG-Tune NEXTGEAR Note i300Link かコストパフォーマンスのHP Pavilion dv6-7000Link に絞ってたのですが、そこから結構悩んだ末軽さを選ぶことに。(さすがに金に糸目をつけずRetinaディスプレイMac book ProLink に行こうとは思わなかった^_^;)

ACアダプタがでかいと言われていたのですが、先代 Vostro 1500Link のそれより少し小さいくらい。もっと大きいのかと思ってたので拍子抜け。Vostro 1500と同じ90W出力なんだからこれくらいしょうがないと思う。

もうひとつの気がかりだった光沢液晶は反射低減フィルムLink を貼ってみたのですが、効果はあるものの本物のノングレアのようにはならず(まぁ技術的に難しいとは思うけど)

しかしノートPCにデスクトップPC用のマニュアル付けてくるのはさすがマウスコンピュータLink 。Acerの紙ぺら1枚よりマシだし私は読まないから、その分コストダウンにまわしてくれればいい。

同時にメモリとSSDも購入し換装する気満々。写真は一回もOS起動されることなく換装されるHDDをバックアップしている最中のもの。


— posted by mu at 04:46 pm   commentComment [3]  pingTrackBack [0]

RGB24bit→Gray8bit変換高速化

C# 4.0でRGB24bitの画像をGrayscale 8bitに変換する必要ができたのですが、調べ方が悪いのか.NET Frameworkに該当する機能は見当たらず、自力変換しているケースがちらほら。

というわけで、以下のように画像データをbyte[]配列に放り込み実験。
JpegBitmapDecoder jpegDecoder = new JpegBitmapDecoder(
    new Uri("4288x2848.jpg"),
    BitmapCreateOptions.PreservePixelFormat,
    BitmapCacheOption.OnLoad);

BitmapSource InputBitmap = jpegDecoder.Frames[0];
int InputStride = InputBitmap.PixelWidth * 3;
if ((InputBitmap.PixelWidth * 3) % 4 > 0) InputStride += 4 - (InputBitmap.PixelWidth * 3) % 4;
byte[] InputData = new byte[InputStride * InputBitmap.PixelHeight];
InputBitmap.CopyPixels(InputData, InputStride, 0);

int OutputStride = InputBitmap.PixelWidth;
if( InputBitmap.PixelWidth % 4 > 0) OutputStride += 4 - InputBitmap.PixelWidth % 4;
byte[] OutputData = new byte[OutputStride * InputBitmap.PixelHeight];

ここではWPF系のBitmapSourceを使ってますが本質は画像データをbyte[]配列に入れることなので、System.Drawing.Bitmapを使う場合は、Bitmap.LockBitsとMarshal.Copyを駆使して下さいませ。

で、安直に以下の二重ループで変換すると、4288x2848ドットの画像を変換するのに約960msec (@Core i7 920)。今回の要件でこの速度はちと不味い。
for (int y = 0; y < InputBitmap.PixelHeight; y++)
{
    for (int x = 0; x < InputBitmap.PixelWidth; x++)
    {
        double intensity =
            0.29891 * InputData[y * InputStride + x * 3] +
            0.58661 * InputData[y * InputStride + x * 3 + 1] +
            0.11448 * InputData[y * InputStride + x * 3 + 2];
        OutputData[y * OutputStride + x] = (byte)intensity;
    }
}
で、半日こねくり回してたどり着いたのが以下のコード。同条件で約50msec。
Parallel.For(0, InputImage.PixelHeight, y =>
{
    for (int InputOffset = y * InputStride, OutputOffset = y * OutputStride; OutputOffset < (y + 1) * OutputStride; )
    {
        int intensity =
            313430 * InputData[InputOffset++] +
            615105 * InputData[InputOffset++] +
            120041 * InputData[InputOffset++];
        OutputData[OutputOffset++] = (byte)(intensity >> 20);
     }
});
  • byte[]配列のインデックスを和積(x+y*stride)でなくint値を増加させると、倍速くらいに
  • intensityをdoubleじゃなくintで計算するのは10%程度の効果。8bitの結果を得るのに20bitで計算するのは明らかに過剰ですが、別にintに収まっているうちは何bitにしても速度変わらないし…
  • 最も効いたのがParallel.For。Core i7 920が4コア+HTなので、4~5倍速はあるかなと思ったのですが、それをはるかに超える上がり方。これがCore2 Duo 2.4GHz上だと20倍速以上7~8倍速程度になるようなのですが、理由がわからない(^^;)

もちろん上記コードが最速とは思っておらず、unsafeやC++を使えばもっと速くできそうですが、Parallel.For使った結果で目的は達してしまったのでやめた次第。

今回はParallel.Forがうれしい誤算だったわけですが、逆にシングルスレッド処理は何をやっているんだという疑問が…

[参考] [.NET]いまさら?Parallel.Forを使ってみた(その1) (GDD Blog 2010/9/25)Link

[2012/4/26 追記] 同じプログラムを改めてCore2 Duoで走らせると両者の速度差は約7.4倍。それでも2コアじゃ説明付かない速度差ですが。

[2012/4/27 追記] 以下のUnsafeコードだとさらに10%速い(44~45msec@Core i7 920、Parallel.For部分のみの計測)。ここまでする必要があるかどうかはケースバイケースかな。

IntPtr srcData = Marshal.AllocHGlobal(InputStride * InputImage.PixelHeight);
IntPtr dstData = Marshal.AllocHGlobal(OutputStride * InputImage.PixelHeight);
try
{
    InputImage.CopyPixels(System.Windows.Int32Rect.Empty, srcData, InputStride * InputImage.PixelHeight, InputStride);

    Parallel.For(0, InputImage.PixelHeight, y =>
    {
        unsafe
        {
            for (byte* pInput = (byte*)srcData + y * InputStride, pOutput = (byte*)dstData + y * OutputStride;
                pOutput < (byte*)dstData + (y + 1) * OutputStride; )
            {
                int intensity =
                    313430 * *pInput++ +
                    615105 * *pInput++ +
                    120041 * *pInput++;
                *pOutput++ = (byte)(intensity >> 20);
            }
        }
    });
}
finally
{
    Marshal.FreeHGlobal(srcData);
    Marshal.FreeHGlobal(dstData);
}

[2012/4/28 追記] Unsafe版のコードに余計なものが入っていたので整理

[2012/5/1 追記] Strideの計算を訂正

— posted by mu at 11:20 pm   commentComment [0]  pingTrackBack [0]

WPF DataContext内でコレクション操作すると例外

一文で書くとなんかややこしく感じたので箇条書き。

  • Windows XP x86/.NET Framework4のWPFで(他の環境は未確認)
  • DataContext内からCollectionChangedイベントが飛ぶような操作を(調べてないけどたぶん他の依存プロパティも)
  • Dispatcherスレッド以外ですると
  • NotSupportedExceptionになる。
例えば、こんなクラスDataのインスタンスをDataContextにセットしてあったとします。
public class Data :INotifyPropertyChanged
{
    public ObservableCollection<Container> _Collection;
    public ObservableCollection<Container> Collection
    {
        get { return _Collection; }
        set
        {
            _Collection = value;
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection"));
        }
    }             
}

public class Container : INotifyPropertyChanged
{
    private string _Value;
    public string Value
    {
        get { return _Value; }
        set
        {
            _Value = value;
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Value"));
        }
    }
}
で、以下の操作をDispatcherスレッド以外からすると、
Data dataContext = (Data)DataContext;
IList<Container> collection = datContext.Collection;

// OK
dataContext.Collection = new ObservableCollection<Container>(
    new Container[] {
        new Container() { Value = "Value1" },
        new Container() { Value = "Value2" }
    }
);

// OK
collection[0].Value = "Value1";
collection[1].Value = "Value2";

// NotSupportedException
collection.Clear();

// NotSupportedException
collection.Add(new Container() { Value = "Value1" });

// NotSupportedException
collection[0] = new Container() { Value = "Value1" };

OKのコードがと例外が出るものとの違いは、プロパティの中身を総書き換えするためCollectionChangedではなくPropertyChangedが飛ぶからと思われます。コレクション操作でも例外が飛ばなくなれば便利なのですが、foreach内で回している最中のIEnumerableにアイテムを追加・削除できないのと同様、改善は期待できないような気がする…

参考リンクで回答されているようにDispatcher.Invokeを使えば何でもできるのですが、MVVMの構造を崩しかねない(*1)のでコレクションを総交換するかコレクション内の各アイテムの値をちまちま書き換えてPropertyChangedイベントで対処した方がいいかなぁ。

(*1) Viewにコレクション操作用のメソッド追加したり、ModelやViewModelがDispatcherを得ようとしたり

[参考] Where do I get a thread-safe CollectionView? (Stackoverflow)Link

[2013/4/4 追記] 続きLink

— posted by mu at 09:00 pm  

System.Drawing.BitmapをWPF用に変換

.NETで画像リソースやOpenCVLink の.NETラッパーEmguLink から画像データを得ようとするとSystem.Drawing.Bitmapインスタンスになるのですが、WPFで直接読み込む方法がないようです。というわけで変換になるのですが、これがちと曲者っぽいので備忘録。

Stream経由

コードが短くて済むのは一旦StreamにBMP形式(昨今メモリは潤沢にあるのでPNG等圧縮する必要はないかと)で保存、それを読みこませる方法。

using System.Windows.Controls;
using System.Drawing;
using System.IO;
(省略)
Bitmap bmp = (何か);
using (Stream st = new MemoryStream())
{
    bmp.Save(st, ImageFormat.Bmp);
    st.Seek(0, SeekOrigin.Begin);
    image1.Source = BitmapFrame.Create(st, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}

この方法が曲者なのはBitmapFrame.CreateがStream読み込みを同期でするのか非同期なのかよく分からない所。非同期で上のコードだと読み込み完了前にStreamが破棄される可能性が高いので失敗(例外は出ず画像が表示されない)。とは言え読み込み終わってもStreamは自動的には閉じられないようなので、放おっておくとガベージコレクション任せということでしょうか。

ネット上の画像を同期読み込みするとBitmapFrame.Createで処理がブロックされるため非同期にするのでしょうけど、同期と非同期の切替条件が今ひとつはっきりしない。私の環境(Windows7 x64、Visual C# 2010 Express、.NET Framework 4 Client Profile)だとFileStreamとMemoryStreamは同期、WebResponseから得るStreamは非同期になるようでしたが、環境や将来的に変わるかもしれない。

非同期になると読み込み中はBitmapFrameインスタンスのIsDownloadingがtrueになり、完了時にDownloadCompletedイベントが飛ぶ(同期だと飛ばない)みたいなのでプロパティをチェックしてイベントハンドラでStreamを閉じるという方法もありますが、イベントハンドラをセットする前に読み込みが完了する可能性がゼロではないため、ちと不安。まぁCreateの直後にやればまず大丈夫でしょうけど。

HBitmap経由

Unmanagedに抵抗がなければ、こちらの方がある意味悩まなくて済むかと。

using System.Windows.Controls;
using System.Drawing;
using System.IO;
(省略)
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
(省略)
Bitmap bmp = (何か);
IntPtr hBitmap = bmp.GetHbitmap();
try
{
    image1.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        hBitmap,
        IntPtr.Zero,
        Int32Rect.Empty,
        System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
    DeleteObject(hBitmap);
}

LateBoundBitmapDecoderLink なんてもの別途用意しているんだから、自動で同期・非同期を切り替えるのはやらなくていいと思う、個人的には。

[参考]

— posted by mu at 01:05 pm   commentComment [2]  pingTrackBack [0]

T: Y: ALL: Online:
ThemeSwitch
  • Basic
Created in 0.1889 sec.
prev
2024.11
next
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30