2009年11月22日 (日)

多少の誤差を無視させ同じ値と判定させるには!?

処理を作成した時に、どんな入力値に対しても
正しく動作しているかを調べるのに苦労する。

入力値を変えながら100回ぐらい結果を出してみる。
その数値を眺めても結果が正しいかなんてわからん!

処理が単純ならエクセルなんかで検算するのも手だ。
図にするのも良いだろう。

だが演算が複雑で入力する値が多い場合や
数百万回の動作確認を行いたい場合には、
別なアプローチで作成した処理の結果と比較するのが
有効な手段だろう。

同じ値を入力すれば、同じ結果になる。

違った場合には、どっちかの処理がバグっている訳だ。

二つの処理を作らなくてはならないが
バグの発見がグッと楽になる。

100万回ぐらいの試行錯誤もPC任せで出来る。
出力結果が異なった部分だけ抜き出して検証すれば良い訳だ。

しかし、処理中の数値の変換過程やらなんやらで
出力結果が微妙に違ったりする!

まあ、誤差と言う奴でしょうか…。

整数(『int』)なら大抵これで終わりなはずだ。

if(a==b){/* 同じ値だ!*/}

だが、浮動小数点(『float/double』で同じ物かを判断する場合、
大抵こんな風に小数点以下を切って比較するだろう。

if((int)a==(int)b){/*同じ値だ!*/}
if((int)(a*100)==(int)(b*100)){/*同じ値だ!*/}

上記は、『a/b』の有効桁が固定している場合には良いのだが
出力結果が30~-30桁なんて幅のある数値だと
上記のような処理では、対応できない…。

で、どうしたら良い物か考えた…

ハッ!と閃いた!

元の値と差分の値の桁数を比較すればOKじゃねぇ?

基本の考え方は、以下の通り。

1.同じものなら差分は、0
2.ほぼ同じものなら桁数は同じ
3.ほぼ同じものなら差分は元の桁数より桁違いに小さい

『3』の『桁違いに小さい』がポイントね!

ちなみに-7桁とかの小さ過ぎる数値は、0とみなす。
0に限り無く近いと誤差が大きくなり過ぎて比較できないので。

で、桁数で同じかどうか判定する処理を作ってみた。

#include    <windows.h>
#include    <stdio.h>
#include    <math.h>
#define        BORDER_ZERO        -7    // ゼロとみなす桁数
#define        BORDER_DIFFER    -4    // 誤差として切り捨てる桁数
BOOL PlaceComp( double a, double b )
{
    double    c, la, lb, lc;

    c    = a - b;
    if( c == 0 )
        return TRUE;    // 同じ物です。
    la    = a == 0 ? BORDER_ZERO : log10( fabs( a ) );
    lb    = b == 0 ? BORDER_ZERO : log10( fabs( b ) );
    lc    = c == 0 ? BORDER_ZERO : log10( fabs( c ) );
    if( ( la <= BORDER_ZERO ) && ( lb <= BORDER_ZERO ) )
        return TRUE;    // 小さすぎる値は、ゼロとみなします。
    if( ( int )la != ( int )lb )
        return FALSE;    // 桁数が異なる場合、明らかに違う値です。
    if( ( int )lc <= ( ( int )la + BORDER_DIFFER ) )
        return TRUE;    // 差分の桁数が十分小さいなら同じものとします。
    return FALSE;
}
void main( void )
{
    double    table[][2]    =
    {
        {    -2.2494911121205649e-031,    7.4983033152370995e-032,    },
        {    3.8268344402313232,            3.8268344402313232,            },
        {    1.0800063947812803e-015,    1.0800063947812803e-015,    },
        {    -9.4531962702149031e-008,    0.00000000000000000,        },
        {    6.2361249923706055,            6.2361254692077637,            },
        {    2.7059803009033203e21,        2.7059805393218994e21,        },
        {    3.1259803009033203e21,        3.1269805393218994e21,        },
    };
    int        i, f;
    for( i = 0 ; i < sizeof( table ) / sizeof( *table ) ; i++ )
    {
        f    = PlaceComp( table[i][0], table[i][1] );
        printf( f ? "same\n" : "differ\n" );
    }
}

結果
same
same
same
same
differ
same
differ

『BORDER_DIFFER』を『-6』にすると『differ』が一つ減る。
『-3』にすると全て『same』になる。

誤差がどの程度かで『define』の設定値を合わせて変えればOKなはず。

実際、必要に迫られたので作ってみたけど結構使えるかも…。

取り敢えずメモとして書いておこう…。

| | コメント (0) | トラックバック (1)

2009年10月20日 (火)

プログレスバーの中に文字を表示したい!

プログレスバーの中に文字を表示したい!

のでAPIを調べてみる…。

だがAPIでは、サポートされていないようだ…。

で、投げていた…。

所が、何か降って来て閃いた!

だったらプロシージャを乗っ取れば良いんじゃない?

それならフックかな?

てな訳で調べれてみた…。

そしたらサブクラス化にすれば良いじゃん!

って事が分かった…。

あ!サブクラス化を完全に忘れてた…。

ここまで来たら直ぐ出来るよ!

※ソースは『Microsoft Visual C++ 6.0』だよ。

//--------------------------------------
#pragma comment( lib, "comctl32.lib" )
//--------------------------------------
#include    <Windows.h>
#include    <CommCtrl.h>
//--------------------------------------
WNDPROC        OriginalProc;
//--------------------------------------
LRESULT CALLBACK ProgressBarProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    LRESULT    Result    = CallWindowProc( OriginalProc, hWnd, uMsg, wParam, lParam );
    if( uMsg == WM_PAINT )
    {
        LPSTR    String    = "push A button!";
        HDC    DC    = GetDC( hWnd );
        SetTextColor( DC, 0x00FFFFFF );
        SetBkMode( DC, TRANSPARENT );
        TextOut( DC, 0, 0, String, strlen( String ) );
        ReleaseDC( hWnd, DC );
    }
    return Result;
}
//--------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    static HWND        hProgress;
    switch( uMsg )
    {
    case WM_CREATE:
        InitCommonControls( );
        hProgress        = CreateWindowEx( 0, PROGRESS_CLASS, "", WS_CHILD | WS_VISIBLE | PBS_SMOOTH, 40, 20, 200, 24, hWnd, 0, NULL, NULL );
        OriginalProc    = ( WNDPROC )SetWindowLong( hProgress, GWL_WNDPROC, ( LONG )ProgressBarProc );
        break;

    case WM_KEYUP:
        if( wParam == VK_ESCAPE )
        {
            DestroyWindow( hWnd );
        } else
        if( wParam == 'A' )
        {
            SendMessage( hProgress, PBM_SETRANGE, 0, MAKELPARAM( 0, 100 ) );
            for( int i = 0 ; i <= 100 ; i++ )
            {
                SendMessage( hProgress, PBM_SETPOS, i, 0 );
                Sleep( 10 );
            }
        }
        break;

    case WM_DESTROY:
        SetWindowLong( hProgress, GWL_WNDPROC, ( LONG )OriginalProc );
        PostQuitMessage( 0 );
        break;

    default:
        return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }
    return 0;
}
//--------------------------------------
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE, LPTSTR, int nCmdShow )
{
    LPSTR            name    = "sample";
    MSG                msg;
    HWND            hWnd;
    WNDCLASS        wc;

    memset( &wc, 0, sizeof( wc ) );
    wc.style            = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc        = WndProc;
    wc.hInstance        = hInstance;
    wc.hbrBackground    = ( HBRUSH )( COLOR_3DFACE + 1 );
    wc.lpszMenuName        =
    wc.lpszClassName    = name;
    RegisterClass( &wc );
    hWnd    = CreateWindow( name, name, WS_OVERLAPPEDWINDOW
, 100, 100, 290, 80, NULL, NULL, hInstance, NULL );
    ShowWindow( hWnd, nCmdShow );
    UpdateWindow( hWnd );
    while( GetMessage( &msg, NULL, 0, 0 ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    return( msg.wParam );
}

取り敢えず目的の第一弾は、達成した!

バーの重なる部分の文字の色変えやセンター合わせなど細工は第二段だね。

ここまで来たら後少し。

もうチョイ上等な処理は、こんな感じか?

//--------------------------------------
#pragma comment( lib, "comctl32.lib" )
//--------------------------------------
#include    <Windows.h>
#include    <CommCtrl.h>
//--------------------------------------
WNDPROC        OriginalProc;
char        ProgressBarText[30]    = "push A or B button!";
//--------------------------------------
#if 0
//    テキストだけ後付けの処理(比較用に用意)
LRESULT CALLBACK ProgressBarProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    LRESULT    Result    = CallWindowProc( OriginalProc, hWnd, uMsg, wParam, lParam );
    if( uMsg == WM_PAINT )
    {
        int        Length, X, Y, P, R;
        SIZE    Size;
        RECT    Rect, RectIn, RectOut;
        HRGN    RgnIn, RgnOut;
        HDC    hDC    = GetDC( hWnd );
        GetClientRect( hWnd, &Rect );
        // ※補正
        SetRect( &Rect, Rect.left + 1, Rect.top + 1, Rect.right    - 2, Rect.bottom - 1 );
        Length    = strlen( ProgressBarText );
        SetBkMode( hDC, TRANSPARENT );
        GetTextExtentPoint32( hDC, ProgressBarText, Length, &Size );
        X    = ( Rect.right - Size.cx ) / 2;
        Y    = ( Rect.bottom - Size.cy ) / 2;
        P    = SendMessage( hWnd, PBM_GETPOS, 0, 0 );
        R    = SendMessage( hWnd, PBM_GETRANGE, FALSE, 0 );
        P    = Rect.left + ( Rect.right - Rect.left ) * P / R;
        CopyRect( &RectIn, &Rect );
        CopyRect( &RectOut, &Rect );
        RectIn.right    = RectOut.left    = P;
        RgnIn    = CreateRectRgnIndirect( &RectIn );
        RgnOut    = CreateRectRgnIndirect( &RectOut );
        SetTextColor( hDC, 0x00FFFFFF );
        SelectClipRgn( hDC, RgnIn );
        TextOut( hDC, X, Y, ProgressBarText, Length );
        SetTextColor( hDC, 0x00000000 );
        SelectClipRgn( hDC, RgnOut );
        TextOut( hDC, X, Y, ProgressBarText, Length );
        ReleaseDC( hWnd, hDC );
    }
    return Result;
}
#else
//    ペイントを乗っ取って表示する処理
LRESULT CALLBACK ProgressBarProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    if( uMsg == WM_PAINT )
    {
        int        Length, X, Y, P, R;
        SIZE    Size;
        RECT    Rect, RectIn, RectOut;
        HRGN    RgnIn, RgnOut;
        PAINTSTRUCT    PaintStruct;
        HDC        hDC;
        hDC    = BeginPaint( hWnd, &PaintStruct );
        GetClientRect( hWnd, &Rect );
        // ※補正
        SetRect( &Rect, Rect.left + 1, Rect.top + 1, Rect.right    - 2, Rect.bottom - 1 );
        Length    = strlen( ProgressBarText );
        SetBkMode( hDC, TRANSPARENT );
        GetTextExtentPoint32( hDC, ProgressBarText, Length, &Size );
        X    = ( Rect.right - Size.cx ) / 2;
        Y    = ( Rect.bottom - Size.cy ) / 2;
        P    = SendMessage( hWnd, PBM_GETPOS, 0, 0 );
        R    = SendMessage( hWnd, PBM_GETRANGE, FALSE, 0 );
        P    = Rect.left + ( Rect.right - Rect.left ) * P / R;
        CopyRect( &RectIn, &Rect );
        CopyRect( &RectOut, &Rect );
        RectIn.right    = RectOut.left    = P;
        RgnIn    = CreateRectRgnIndirect( &RectIn );
        RgnOut    = CreateRectRgnIndirect( &RectOut );
        SetTextColor( hDC, 0x00FFFFFF );
        SelectClipRgn( hDC, RgnIn );
        FillRect( hDC, &RectIn, ( HBRUSH )COLOR_HIGHLIGHTTEXT );
        TextOut( hDC, X, Y, ProgressBarText, Length );
        SetTextColor( hDC, 0x00000000 );
        SelectClipRgn( hDC, RgnOut );
        TextOut( hDC, X, Y, ProgressBarText, Length );
        EndPaint( hWnd, &PaintStruct );
        return 0;
    }
    return CallWindowProc( OriginalProc, hWnd, uMsg, wParam, lParam );
}
#endif
//--------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    static HWND        hProgress;
    switch( uMsg )
    {
    case WM_CREATE:
        InitCommonControls( );
        hProgress        = CreateWindowEx( 0, PROGRESS_CLASS, "", WS_CHILD | WS_VISIBLE | PBS_SMOOTH, 40, 20, 200, 24, hWnd, 0, NULL, NULL );
        OriginalProc    = ( WNDPROC )SetWindowLong( hProgress, GWL_WNDPROC, ( LONG )ProgressBarProc );
        break;
    case WM_KEYUP:
        if( wParam == VK_ESCAPE )
        {
            DestroyWindow( hWnd );
        } else
        if( wParam == 'A' )
        {
            strcpy( ProgressBarText, "*********************" );
            InvalidateRect( hProgress, NULL, TRUE );
            SendMessage( hProgress, PBM_SETRANGE, 0, MAKELPARAM( 0, 100 ) );
            for( int i = 0 ; i <= 100 ; i++ )
            {
                SendMessage( hProgress, PBM_SETPOS, i, 0 );
                Sleep( 10 );
            }
        } else
        if( wParam == 'S' )
        {
            SendMessage( hProgress, PBM_SETRANGE, 0, MAKELPARAM( 0, 100 ) );
            for( int i = 0 ; i <= 100 ; i++ )
            {
                wsprintf( ProgressBarText, "** %3d", i );
                InvalidateRect( hProgress, NULL, TRUE );
                SendMessage( hProgress, PBM_SETPOS, i, 0 );
                Sleep( 10 );
            }
        }
        break;

    case WM_DESTROY:
        SetWindowLong( hProgress, GWL_WNDPROC, ( LONG )OriginalProc );
        PostQuitMessage( 0 );
        break;

    default:
        return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }
    return 0;
}
//--------------------------------------
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE, LPTSTR, int nCmdShow )
{
    LPSTR            name    = "sample";
    MSG                msg;
    HWND            hWnd;
    WNDCLASS        wc;

    memset( &wc, 0, sizeof( wc ) );
    wc.style            = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc        = WndProc;
    wc.hInstance        = hInstance;
    wc.hbrBackground    = ( HBRUSH )( COLOR_3DFACE + 1 );
    wc.lpszMenuName        =
    wc.lpszClassName    = name;
    RegisterClass( &wc );
    hWnd    = CreateWindow( name, name, WS_OVERLAPPEDWINDOW
, 100, 100, 290, 80, NULL, NULL, hInstance, NULL );
    ShowWindow( hWnd, nCmdShow );
    UpdateWindow( hWnd );
    while( GetMessage( &msg, NULL, 0, 0 ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    return( msg.wParam );
}

出来てしまえば、出来なかった自分が阿保に思えるから不思議…。(汗)

ここまで出来れば、後はクラス化してまとめれば直ぐだ♪

サブクラス化を思い出せば直ぐだったなぁ~。

でも、また忘れそうなので記録しておこう!

| | コメント (0) | トラックバック (0)

2009年10月 8日 (木)

『UIActionSheet』を使う時にチョットはまった…。

『iPhone』のプログラミングしていたら…
『UIActionSheet』の所でチョットはまった…。

最終的に分かった事は、プロジェクトを
『Windows-based Application』で作ったのが原因らしい…。

『View-based Application』で作った場合以下のプログラム動作する。

- (IBAction)pushDoButton:(id)sender {
    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"やるの?" delegate:self cancelButtonTitle:@"やめる" destructiveButtonTitle:@"やる" otherButtonTitles:nil];
    [actionSheet showInView:self.view];
    [actionSheet release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    if(buttonIndex!=0) {
        // 『やる』処理
    }
}

所が『Windows-based Application』だと、
まず『[actionSheet showInView:self.view];』が
エラーになる…。

最初、何でエラーが出るか分からなかったが…
色々なんかした結果『View-based Application』で
作られるビューだと気が付いた…。

で『[actionSheet showInView:self.window];』と
『window』にする事で解決したのだが…。

『delegate:self』の所で警告が出ていたので
『delegate:nil』にして警告を消していたら
『actionSheet』が呼び出されない…。

こんな警告…。
>warning: class 'xxxxApplicationDelegate' does not implement the 'UIActionSheetDelegate' protocol

『delegate:self』にすれば警告は出るが動く…。

どこかで設定し忘れているらしい…。

あ!ヘッダーを設定するのかぁ!?

とヘッダーを
>@interface xxxxAppDelegate : NSObject <UIApplicationDelegate> {
から
>@interface xxxxAppDelegate : NSObject <UIApplicationDelegate, UIActionSheetDelegate> {
に書き換えたら警告が出なくなった…。

最終的にこうなった。
xxxxAppDelegete.h
@interface xxxxAppDelegate : NSObject <UIApplicationDelegate, UIActionSheetDelegate> {
xxxxAppDelegete.m
- (IBAction)pushDoButton:(id)sender {
    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"やるの?" delegate:self cancelButtonTitle:@"やめる" destructiveButtonTitle:@"やる" otherButtonTitles:nil];
    [actionSheet showInView:self.view];
    [actionSheet release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    if(buttonIndex!=0) {
        // 『やる』処理
    }
}

柔軟なのは、良いけど…
分かり難いよ…。

初心者なんでRPG宜しく経験値を稼いでいる所…。(汗)

| | コメント (0) | トラックバック (0)

2009年7月25日 (土)

負の数の表現…『0x80000000』は、駄目?

負の数を16進数とかで表現したい場合がある。
平たく言えば十進数に直すのが面倒なのだ…。

例えば16ビットサウンドでのボリュームの変更処理。
オーバーフロー対策で32ビットの『int/long』で処理するのが普通だと思う。
このオーバーフローをチェックするとこんな感じになるかと思う。

short    *WaveData;
double    Volume;
int    Wave    = *WaveData;
    Wave = ( int )( Wave * Volume );
    if( Wave < -32768 )
        Wave    = -32768;
    if( Wave > 32767 )
        Wave    = 32767;

でも『-32768 / 32767』なんて覚えないので…

    if( Wave < 0xFFFF8000 )
        Wave    = 0xFFFF8000;
    if( Wave > 0x00007FFF )
        Wave    = 0x00007FFF;

なんて書いてしまうと上手く動かない…。
そこで…

    if( Wave < ( int )0xFFFF8000 )
        Wave    = 0xFFFF8000;
    if( Wave > ( int )0x00007FFF )
        Wave    = 0x00007FFF;

と書くと上手く動く…。

謎だ…。(汗)

なので調べてみた…。

#include<stdio.h>
void main( void )
{
    __int64            i;
    int                j;
    short            k;
    for( i = j = k= 0 ; i < 0x100000000 ; i += 0x4000, k += 0x2000 )
    {
        j    = k;
        if( j < 0xFFFF8000 )
            printf( "8000:%08X\n", j );
        if( j > 0x00007FFF )
            printf( "7FFF:%08X\n", j );
    }
}

結果は、以下の値をループする。
8000:00000000
8000:00002000
8000:00004000
8000:00006000
...

マシン語で見てみると…。
51:           if( j < 0xFFFF8000 )
00401077   cmp         dword ptr [ebp-0Ch],0FFFF8000h
0040107E   jae         main+81h (00401091)

符号無しで比較している!(汗)
右側優先?それとも符号無し優先?

で『if( j < 0xFFFF8000 )』を『if( j < ( int )0xFFFF8000 )』にすると…

51:           if( j < ( int )0xFFFF8000 )
00401077   cmp         dword ptr [ebp-0Ch],0FFFF8000h
0040107E   jge         main+81h (00401091)

符号有りで比較している!

試しに『if( 0xFFFF8000 > j )』とかにしてやってみる…
が、結果変わらず…。

なので符号有りと符号無しの比較では、符号無しで判定されるようだ…。

確認の為に『unsigned int』でも確認してみる。

unsigned int l = 0xFFFF8000;
    ...
    if( j < l )
    ...

この場合…
『warning C4018: '<' : signed と unsigned の数値を比較しようとしました。』と
きっちり警告がでる!
構わず実行してマシン語を確認すると符号無しで比較していた。

ちなみにこうするとちゃんと符号付きで処理される。

int l = 0xFFFF8000;
    ...
    if( j < l )
    ...

多分、符号無し優先で判定するのは、仕様なのだろう。
でも、リテラル値が符号無しなら警告を出しても良いんじゃない?

こんな使い方をする奴なんかきっと想定外だったのかもしれない…。

結論!

負の数の表現…『0x80000000』は、駄目?

は、駄目!だった…。

2009年7月25日追記----------------

ちなみに『Microsoft Visual Studio 6.0』での事だ。
でも他でも起こるんだろうな…。

| | コメント (0) | トラックバック (0)

2009年6月 5日 (金)

『Direct3D』と『DirectShow』の狭間で!

『Direct3D』でフルスクリーン表示をした時に
『DirectShow』での画像が表示されない事がある…。

ウインドウでは起きない…。

『DirectShow』のエラーも出ていない…。

何が悪いんだろう?

ちなみに現象が起きた条件。
・C++
・DirectX8
・Direct3D
・DirectShow
・フルスクリーン表示

一度画像が再生されないともう再生されないかと思ったら
そうでもない。

何かがハングとかしている訳ではなさそうだ…。

取り敢えず傾向を見るために100回ぐらい
ムービー再生のテストをしてみる…。

どうも二分の一の確立で表示されないようだ…。

これは、どういう事だろうか?

ムービーが表示される画面と表示されない画面があって
その二つの画面が高速に入れ替わっているように思える…。

そのタイミングによって表示されたり表示されなかったり…。

多分『Direct3D』が悪さをしているんだろう…。

ありあえるのは、フリップによるフロントサーフェスと
バックサーフェスの切り換えが影響していると
当たりを付けてみる。

ざっくり3D出力関連をこんな感じにコメントアウトしてみる。

#if 0
Device->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER, 0, 1.0f, 0 )
Device->BeginScene( );
...
Device->EndScene( );
Device->Present( NULL, NULL, NULL, NULL );
#endif

問題無くムービーが再生される…。

どうやら「Clear/BeginScene/Present」のどれかが呼ばれると
フリップが起こり表示されなくなるようだ…。

ムービー再生中に『Present』を連続して呼び出すと再生される…。

無論、黒い画面が入ってパカル訳だが傾向は、得られたと思う。

結論、『Direct3D』が悪さしている!<と断定!

実際、ここまで来るのに数日間かけて結構試行錯誤…。(涙)

『DirectShow』は、ウインドウ周りでの処理で『HDC』を介して『BitBlt』
辺りでデスクトップに貼り付けているのではないだろうか?

と推測。

何らかの理由で『HDC』の出力先が切り替わっているのでは?

だったら必ず表示される方にしてムービーを再生すればOKじゃないのか?

どうやったら2つのサーフェスのどちらが表かを区別できるのだろう?

調べてみる…。

どうやら、そのような関数は、無いようだ…。

ヘルプには、「良い感じに切り換えている」とある…。

カウンターを付けてみたが単に傾向が何となく分かるだけで
間違いなくこっちと言えなさそうだ…。

もし、『DirectShow』が『HDC』経由で何処かに出力されているのなら
表示されなくても画像はあるだろう…。

と『HDC』経由で画像を引っ張り出してテクスチャーに張って
表示できるようにしたのだが…

メチャメチャ重い…。

それに表示されない時には、表示されない…。

まあ、何かミスってる可能性は、大だが…。

正直重過ぎて使い物にならないので没!

『HDC』での出力と『Direct3D』との出力で齟齬があれば
それを手掛かりに確認できるかも?

何処に出力されているのか分からないので『HDC』経由で
プット&ゲットしてみるが、同じ物が返ってくる…。

テストを行っていると不思議な現象を見付けた。

『HDC』経由で画面に出力したら即座に表示される時と
表示されない時がある。

もしかしたら、これが『HDC』と『Direct3D』の出力の齟齬!?

『HDC』でプットして『Direct3D』でゲットして異なれば正常とか…。

一条の希望の光を見えたような気がした…。

試してみた…。

『HDC』経由で画面に出力して即座に表示される時には、
『GetRenderTarget』で画像を得られない。

『HDC』経由で画面に出力しても即座に表示されない時には、
『GetRenderTarget』で画像を得られた!

これでムービーを確実に表示する方法を見付けたよ!

マジ祝杯もんス!

----------------------------------------
検証プログラムを作ろうかと思ったが…
メチャメチャ長くなるので…
チェック処理の所だけね。
説明少ないけど、足りない分は察して頂戴。
今回珍しく動作確認していなので間違いがあるかも…。(汗)
と言う訳でヒント程度に参考にして頂戴…。

処理の流れは、「WindowDC」で画像を出力し
「GetRenderTarget」で画像を取得し
比較すると言ったもの。
----------------------------------------
LPBYTE    Pixel;
----------------------------------------
void RenderTargetCopy( void )
{
    LPDIRECT3DSURFACE8    Render;
    LPDIRECT3DSURFACE8    Bitmap;
    D3DSURFACE_DESC    Desc;
    D3DLOCKED_RECT    Rect;

    Device->GetRenderTarget( &Render );
    Render->GetDesc( &Desc );
    Device->CreateImageSurface( Desc.Width, Desc.Height, D3DFMT_R8G8B8, &Bitmap );
    D3DXLoadSurfaceFromSurface( Bitmap, NULL, NULL, Render, NULL, NULL, D3DX_FILTER_NONE, 0 );
    Bitmap->LockRect( &Rect, NULL, D3DLOCK_READONLY );
    memcpy( Pixel, Rect.pBits, Desc.Height * Rect.Pitch );
    Bitmap->UnlockRect( );
    Bitmap->Release( );
    Render->Release( );
}
----------------------------------------
    // フルスクリーンならフロントサーフェスが後ろに行っていたら前に出します。
    if( FullScreenFlag )
    {
        for( i = 0 ; i < 10 ; i++ )
        {
            // 「BitmapDC」は、画面の大きさの真っ白な画像です。
            // 「WindowDC」は、ウインドウ作成時に取得しておきます。
            BitBlt( WindowDC, 0, 0, Width, Height, BitmapDC, 0, 0, SRCCOPY );

            // レンダリングターゲットの画像を取り出します。
            RenderTargetCopy( );

            // 画像情報が一致したら脱出します。
            if( ( *( LPDWORD )Pixel != 0xFFFFFFFF )
                break;

            // 「Direct3D」を使って画面をクリアします。
            // これでフロントサーフェスがフィリップされます。
            Device->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER, 0, 1, 0 );
            Device->BeginScene( );
            Device->EndScene( );
            Device->Present( NULL, NULL, NULL, NULL );
        }
    }
    // 『DirectShow』のムービー再生処理へ
----------------------------------------

PS.

『HDC』は、バックサーフェスに関連付けられ
『DirectShow』は、フロントサーフェスに関連付けられ
それらの関連付けは、固定されているようです。

従ってバックサーフェスがレンダリングサーフェスの時、
『HDC』でのプットは、画面上に表示されません。
しかしレンダリングサーフェスから画像を得られます。

逆にフロントサーフェスがレンダリングサーフェスの時、
『HDC』でのプットは、画面上に表示されますが
レンダリングサーフェスから画像を得られません。

フィリップによってフロントサーフェスが
レンダリングサーフェスの時には、
『DirectShow』の画像が表示されなくなる
という事になるらしい…。

フィリップに関係無く『HDC』と『DirectShow』は、
画面に出力されるべきじゃない?

これって『Direct3D』の不具合だろう!

…と思うのだが…。

多分『Direct3D』を使っている奴等は、
『HDC』や『DirectShow』を使わないのかもしれない…。

…多分そうに違いない…。

| | コメント (2) | トラックバック (0)

2009年5月 6日 (水)

『NtUserSetWindowsHookEx』って何!?

ウィルスバスター様が不正アクセスだとおっしゃっております…

と後ろの奴に言われた…。

不正アクセス?

何を持って不正アクセスとおっしゃるあなた!

と言う訳でログを確認。

『セキュリティーティレポート』ダイアログに

『不正変更の監視』とある…。

以下ログの内容…。

>種類:
>API Event
>検出ソース/プロセスID:
>NtUserSetWindowsHookEx
>ファイル名:
>C:\...
>該当ポリシー:
>DLL(プログラムライブラリ)インジェクション
>実行された処理:
>拒否

『NtUserSetWindowsHookEx』って…

何かどっかで見たような…?

なので、調べてみたら…

ああ、『SetWindowsHookEx』の事!?

多分、『NtUserSetWindowsHookEx』って

こんな意味ではないかと推測してみる…。

・『Nt』は、『Windows NT 系 OS』で
・『User』は、『User 権限』で
・『SetWindowsHookEx』を実行したよ…。

『SetWindowsHookEx』を大雑把に説明すると
ディスクトップに任意の処理を追加する奴。

多分、『ウイルスバスター』様は、こう言いたいのでは?

と推測してみる…。

>API(アプリケーションプログラムインターフェイス)が
>ユーザー権限で『SetWindowsHookEx』関数を使ったよ。
>でも、そんな事はウイルスバスターが許しません!

どのぐらい正しいか分からん…。

全くの間違いかも?(汗)

まあ、普通のアプリケーションでは

『SetWindowsHookEx』を使用しない。

特殊なAPIの内部で使用する場合がある。

それに引っかかったのかも…。

ちなみに『SetWindowsHookEx』を使うと何ができるかというと
『キーロガー』みたいな監視ソフトが作れたりします…。

は、置いといて…

実際に『不正アクセス』が起こるか試してみた…。

『Windows Vista Business + SP1』に

『ウイルスバスター2008(32bit版)』をインストールして

アプリケーションを実行!

『不正アクセス』は、起きなかったよ!

謎だ?

後ろの奴のPCに何か仕込まれていない?

問題のPCに何か仕込まれていない?

2009年5月6日追記------------------

不正アクセスが発生するのに特定のタイミングがあるそうだ。

それがムービーを再生する時なんだそうだ。

ムービーを再生する時にそのムービーフォーマットに

対応したデコーダーを読み込む。

自分のPCでは、MPEGムービーの再生時に
「clm4splt.ax」をロードしている。
バージョン情報に「CyberLink MPEG-4 Splitter」とある。

このデコーダーは、『プログラム』であるわけで…

ディスクトップに何かをするとなると…

画像を直接ディスクトップに表示する機能まで持っているのかも…?

もしくは、何らかの入力を直接得ようとしているのかも?

もちろんその部分もテストしたけど
不正アクセスを確認できなかった…。

勿論、問題PCでは、別なデコーダーが入っている可能性もある。

最終的には、問題が発生したPCをお借りして
デバッグするしかないです…。

なあ、借りられればの話ですけどね…。

2009年5月9日追記------------------

上記の追記を書いた後、そう言えば…

読み込んでいるDLLを確認する方法があった事を思い出した!

『Microsoft Visual Studio』などの統合開発環境上でEXEを実行すると

実行時に読み込んだDLLなどをリアルタイムに

表示してくれたっけ…。

試してみる…

おお、ガンガン表示されるぞよ!

もしかしたらこれで『不正アクセス』している奴を

見つけられるかもしれない…。

お困りの方は、試してみる価値があるかも!

…無いかも…。

| | コメント (2) | トラックバック (0)

2009年3月11日 (水)

FTPサーバーを立ててみる!

※注意※
ネットワーク関係の説明をはっしょっているので
人によっては訳が分からんかもしれません…。

■最初は、些細な事だった…編

会社でこんな事を言った人が居た。

「FTPサーバーが欲しいなぁ~」
「でも難しいんだよねぇ~」
「セキュリティとかさぁ~」
「スペシャリストが居ないと駄目だよねぇ~」
「無理だよねぇ~」

取り合えず、ググってみると意外に簡単そうだ。
出来そうみたいと伝える。

「まっ、今は、いいよ」

とあっさり却下。

時間があったらやってみてと言う事なので…
で、今!この時!この時間があった!のである!

…いや、ホント…。

■苦闘編

押し入れに転がっているPCを引っ張り出して持ってきた…。
省スペース型だが意外と重かった…。

早速起動!

あれ?起動しない…。
ありゃ!何で!

※この時、三原則を忘れていた…。

蓋を外して見てみる…。

ほこりか?
エアーで飛ばしてみた…。

ビデオカードか?
外して付けてみた…。

戻してケーブルを付け直す時原因を見つけた!

何気にネットワークコネクタにUSBをぶち込んでいた…。(汗)

何かが走馬灯のように見えた気がした…。

ちゃんとUSBコネクタに挿したら動きました。

最近、ちょっと精神失調気味なのかも…。

酒でも飲んで寝よう…。

■もう、七転八倒編

IPアドレスなど設定しなおして…
要らないファイルは、ざっくり消して…
準備を行う。

手軽に使えそうなので『Tiny FTP』を試してみた…。

最終的なファイルの日付が2001年11月なのと
『Windows98』用なのに一応『Window2000』に対応しているらしい。
でも…、ちょっと色々不具合が…。
しかし、このサイトFTPについての説明は分かり易いと思うので
『これは一体何だ?』を読み進めると勉強になるかと思う。
Tiny FTP Daemon

まあ、なんだかんだでローカルで試すと上手くいった。

グローバルアドレスでは、どうだ!

何か上手く行かない…。

ISPによっては、セキュリティ上、グローバルアドレスから
ローカルにアクセスを通さない場合もあるので
出来そうかどうかISPのHPで色々調べてみる。
グローバルからのアクセスは、禁止されて無さそうだ…。
ブロードバンドルータの設定も多分合っている筈…。

まさか…

ブロードバンドルータに何か問題が!?

オムロンのルータ『MR104FH』を使用しているのだが
グーグル先生に『オムロン ルータ MR104FH ファームウェア』で質問すると
これが、なかなかの問題児って事が分かった。

結構、最初の頃は、ファームウェアをチェックしていて
『Ver.1.01A』から『Ver.2.00A』に更新していた。
この『Ver.2.00A』に不具合があり『Ver2.01A』がリリースされたとの
記事を見つけたのでリンク先のダウンローを実行した。

さて、インストールする前に…
事前に全項目のスクリーンショットを取っておいて…
プロパイダーから送られてきた接続設定も用意して…
これが無いと再接続できなくなるからね…。

インストールして初期設定の『192.168.2.1』にアクセスして…
って、あれ?接続できないぞ…?

や…

やばいかも…

あ!IPアドレスのグループが違うじゃん!

LAN:192.168.0.xxx/255.255.255.0
MODEM:192.168.2.1/255.255.255.0
これじゃあアクセスできないよ!
サブネット・マスクの意味を思い出しましたよ…。(汗)

無事アクセスできてました…。
この辺、最低限のネットワーク知識が無いと無理!(笑)

念のために工場出荷状態に戻して…。<お約束

無事、設定も終了しインターネットにアクセスできるようになったが…
『Ver2.01A』は、更に輪をかけて悪かった…。
チョットシステムにアクセスするだけで
DHCPの内容が吹っ飛ぶ!

…何故に?

あっちこっちでIPアドレスのコンフリクトが発生して『ギャー!』だよ!

ちょっと勘弁して欲しい。(泣)

インストールした初日に余りの異常さに
再度ダウンローサイトを確認してみた。

ダウンロードMR104FH

あんぎゃあ!最終版の『Ver.2.04G』があるじゃん!

で、最終版の『Ver.2.04G』入れても駄目だったら…
『Ver.1.01A』に戻すか…。
それとも、黙って買い直しか?(汗)

ファームウエア『Ver.2.04G』を入れたら…

『仮想サーバー』がサクッと動いた…。

あらあらら…。

脱力…。

取り合えず、『「Windows2000』のFTPサーバーを試してみる…

Windows 2000 で FTP サーバーをセットアップする方法

FTPサーバーをセットアップしよう

上記のサイトを参考に色々設定してみた。
これなら問題無く使えそう…。

これでFTPサーバーが構築できる。

ネットワークって色々絡んでいるので
不具合の原因を探し出して潰していくのが大変…。

たまに自分がポカするし…。(汗)

まあ、出来たので良しとしよう!

この後がそれになりにそれなりなのだが…
それは、忘れて…

取り合えず祝杯じゃ!

PS.

自宅からFTPアクセスしてみた。

アップは、110KB/Sで転送できたのだが…
ダウンが60~70KB/Sと明らかに遅い…。
これは、社のADSLがISDNと同一ケーブル中にあるために
信号の干渉が起こり速度が出ないそうだ。
これは、接続時にNTT側から説明があったかと思う。

NTT施設とは、めちゃめちゃ近いので
本来ならフルスピードが出るはずなんだよね…。
ちなみに自宅は、フルスピードですよ♪

で、どこの誰がISDN何かしているかと言えば…
社なんだよね…。

あらあら…。

やっぱ、光だよ!

すれば、アップもダウンも早くなるもんね♪

遠い未来に期待…。

目的は、達成されたので!

祝杯じゃ!

| | コメント (0) | トラックバック (0)

2009年2月21日 (土)

ドラクエ9発売延期の原因…?

ドラクエ9発売延期でこんな記事が!

通信対戦機能にトラブルか? ドラクエ9発売延期の原因を推理する

『新清士のゲームスクランブル』は毎回、楽しく読ませて頂いています。

個人的に共感できる部分が多いのが理由なのだが…

今回のは、久々にトラウマスイッチが入りましたよ!

記事内にこんな記述がある。

>ワンテンポ遅れるような操作感がある。

これは、通信ゲームで複数のキー入力を同時に反映するための仕組みになる。

仮にサーバーで行う場合にも同様なディレイは、発生する。

さらに記事の中でこんな下りがあるのだが…。

> そもそも、日本にはパソコンの対戦ゲーム文化がなかったため、ゲーム用のオンライン対戦プログラムを書ける技術者が少なく、ノウハウも不足している。ハードウエアの性能をギリギリまで引き出すことを要求されるDS用のサーバー技術の開発経験者はなおさら少ないだろう。

実は、コンシューマでは意外に対戦ゲームはあったりする。

また通信部分が全く出来てなかったら販売日が決定していないと思うのだ。

つまりある程度できていたと見るのが筋だろう。

ところが最終的なチェックで原因不明の不具合が発生して

パニックに陥ったと言った所ではないだろうか?

例えば6時間以上遊ぶと同期が取れず

不具合が出る的な内容では無いかと思うのだ。

実は、ここがトラウマだったりする…。

遥か昔、バグが取れないのでヘルプに入ってくれと言われて

放り込まれたのが某ゲームの通信部分だったりする…。

既に担当者は、2ヶ月程缶詰だったかと思う…。

でも、バグが取れないからって…

勘弁してくれよ!

…と言う事でトラウマ…。

2台の機体間の通信なのだが固体によって

CPUの速度が微妙に異なるので長時間対戦すると同期が破綻する。

これが、分からなくて泣いた!

これがノウハウに入るのだと思う。

記事では、『サーバー役』とあるが、多分「よ~い、ドン!」と

合図をする機体だろうと思われる。

処理が同じなら同じ入力で同じ結果が得られる。

つまり個々の処理は、個々の機体行っていると思うのだ。

だが同期を取ってスタートするのだが必ずどちらかが遅れる!

遅れが発生した時にもう一方が待たないと同期が破綻する。

この同期破綻は、2台の速度差が無いと長い事ゲームをしないと発生しない。

つまり機体の出来が良い程不具合が発生しないのだ…。

しかも発生した瞬間を捕らえるのも難しい。

確か入力キーデータに番号を付けて反映キーデータの番号を

確認する形で同期破綻を確認したような気がする。

でも原因が分かれば対応できる内容。

3ヶ月と言うのは、色んな意味で『ああ』と思える延期期間だったりする。(汗)

スタンドアローンでのゲームプログラマーでは、経験しない現象なので

このプログラマーのトラウマになるに違いない!

…もっともこれが原因なら…。

別なゲームの場合にはフロアーが複数になった時に

同期が破綻すると言うのもあった…。

4人参加でダンジョンを攻略すると言うもの。

所が個々のPCには、そのユーザーが存在するフロアーしか存在しない。

最初に次の階のフロアーへ突入したユーザーがバケツリレー的に

フロアーサーバーになると言う変則バケツリレーサーバー?

…これもありそう…。

でもこの問題は、決定的過ぎて製作途中で判明すると思う。

やっぱ、タイミングかと…。

ともかく通信プログラムには、トラウマ食らったのです!

と書いておいてふと気が付いたのがコンシューマには、

品質チェックシートがあったはず。

特にネットワークでの注意点も書いてあるはず…。

その中に固体速度差の対応は、必須で書いているんじゃないかな?

だとすると何だろう?

確か任天堂では対応機種の全種でのチェックがあったかと思う…。

実際DSの内部バージョンが幾つあるか知らないが

全組み合わせでのデバッグテストで

『Jバージョンでは不具合がでます修正してください!』

とかあったのかも…。(汗)

ああ、妄想は果てしない…♪

| | コメント (0) | トラックバック (0)

2009年2月18日 (水)

VRAM容量ってどうやったら分かる!?

パソコンの情報を取得したい場合、どうしたら良いか色々探してみた。

最も簡単なのがレジストリ漁り。

大まかな情報は、レジストリから手に入る。

CPUの速度も得られる。

レジストリエディターで全てセーブしてから検索する早いよ♪

以下の部分で基本的な情報は、フォローできる。

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion]
"ProductName"=OS名
"CurrentBuild"=ビルディング番号
"CSDVersion"=サービスパック
"RegisteredOwner"=ユーザー名
"ProductId"=プロダクト番号

[HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0]
"VendorIdentifier"=CPUメーカー
"Identifier"=CPU型番
"~MHz"=CPUのクロック数

[HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System]
"Identifier"=システム

レジストリーで得られないメモリー容量は、「GlobalMemoryStatus」で得られます。

ドライブの総容量と空き容量などは、「GetDiskFreeSpaceEx」で得られます。

ビデオ名はダイレクトXで得てみる。

D3D = Direct3DCreate8( D3D_SDK_VERSION );
D3D->GetAdapterIdentifier( 0, D3DENUM_NO_WHQL_LEVEL, &VideoIdentifier );

最近のビデオカードの性能でVRAM容量は結構クリティカルな情報。

しかし簡単に取得する方法が無い!

どおしよう…?

どうやったら得られるのやら?

そんなこんなでいろいろ調べて行き当たったのがWMI
(Windows Management Instrumentation)。

色々探して最新版をインストールしたらコンパイルできなかった…。

どうやらバージョンで対応できないらしい…。

『ウィキペディア(Wikipedia)』にVC++6対応のライブラリ情報が!
Visual C++ 6.0に対応した最後の「Platform SDK」です。

と言う事でチョット古いバージョンをこっからダウンロードしましたよ…。
Windows Server 2003 PSDK Full Download with Local Install

※インストールするには、チョット癖がある。
※しかもインストール画面が更に癖がある。

かなり珍しいインストール処理だと思うよ。

ともかくインストールしてコンパイルして動きましたよ♪

//******************************************************************************
//    ビデオ情報の取得
//******************************************************************************
//**************************************
//    inculde
//**************************************
#define _WIN32_WINNT    0x0400
#include    <windows.h>
#include    <wbemcli.h>
#include    <stdio.h>
//**************************************
//    table
//**************************************
LPSTR            VideoMemoryTypeList[]    =
{
    "Other",                            //  0 = その他
    "Unknown",                            //  1 = 不明
    "VRAM",                                //  2 = VRAM
    "DRAM",                                //  3 = DRAM
    "SRAM",                                //  4 = SRAM
    "WRAM",                                //  5 = WRAM
    "EDO RAM",                            //  6 = EDO RAM
    "Burst Synchronous DRAM",            //  7 = バーストシンクロナスDRAM
    "Pipelined Burst SRAM",                //  8 = パイプラインバーストSRAM
    "CDRAM",                            //  9 = CDRAM
    "3DRAM",                            // 10 = 3DRAM
    "SDRAM",                            // 11 = SDRAM
    "SGRAM",                            // 12 = SGRAM
};
//**************************************
//    work
//**************************************
IEnumWbemClassObject    *Enum;
IWbemClassObject    *Object;
IWbemLocator    *Locator;
IWbemServices    *Services;
ULONG            Returned;
//**************************************
//    output
//**************************************
void string_output( const char* item, const OLECHAR* name )
{
    char            string[256];
    VARIANT            variant;
    Object->Get( name, 0, &variant, NULL, NULL );
    WideCharToMultiByte( CP_ACP, 0, variant.bstrVal, -1, string, sizeof( string ), NULL, NULL );
    printf( "%s%s\r\n", item, string );
}
//**************************************
//    ビデオ情報の取得
//**************************************
void main( void )
{
    BSTR            server, wql, path;
    VARIANT            variant;

    server    = SysAllocString( L"\\\\.\\root\\cimv2" );
    wql        = SysAllocString( L"WQL" );
    path    = SysAllocString( L"select * from Win32_VideoController" );

    CoInitializeEx( NULL, COINIT_MULTITHREADED );
    CoCreateInstance( __uuidof( WbemLocator ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IWbemLocator ), ( LPVOID* )&Locator );
    Locator->ConnectServer( server, NULL, NULL, 0L, 0L, NULL, NULL, &Services );
    CoSetProxyBlanket( Services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_DEFAULT );
    Services->ExecQuery( wql, path, WBEM_FLAG_FORWARD_ONLY, NULL, &Enum );
    Enum->Next( WBEM_INFINITE, 1, &Object, &Returned );

    string_output( "AdapterCompatibility    : ", L"AdapterCompatibility" );
    string_output( "VideoProcessor          : ", L"VideoProcessor" );
    string_output( "AdapterDACType          : ", L"AdapterDACType" );
    string_output( "Caption                 : ", L"Caption" );
    string_output( "DeviceID                : ", L"DeviceID" );
    string_output( "DriverVersion           : ", L"DriverVersion" );
    string_output( "InfSection              : ", L"InfSection" );
    string_output( "InstalledDisplayDrivers : ", L"InstalledDisplayDrivers" );

    Object->Get( L"AdapterRAM", 0, &variant, NULL, NULL );
    printf( "AdapterRAM              : %d MB\r\n", variant.lVal / 1024 / 1024 );

    Object->Get( L"VideoMemoryType", 0, &variant, NULL, NULL );
    printf( "VideoMemoryType         : %s\r\n", VideoMemoryTypeList[variant.lVal] );

    Enum->Release( );
    Services->Release( );
    SysFreeString( wql );
    SysFreeString( path );
    SysFreeString( server );
    CoUninitialize( );
}
//******************************************************************************
//    end
//******************************************************************************

上記のプログラムでビデオ関連の情報が得られます。

まあ、何かの参考になれば幸いです♪

PS.

よくよく考えてみればウインドウズは、APIで制御する事を前提にしている。
って事は、それが正常か調べられないといけない訳だ。
その為の仕組みの一つなのだろう。

でも、デバッグ処理に近いのであまり一般に公開していない?
ともかく一般に使い易くは、作っていないみたいです…。

自分のデバッグ処理も今一なので反面教師的な参考にしよう。(笑)

| | コメント (0) | トラックバック (0)

2008年12月12日 (金)

先生!何となく桁外れに遅くなります~!

メモリクリアしているだけなのに特定のPCだと
めちゃめちゃ遅くなるのはどう言う訳?

色々調べても単に転送速度と言うには、遅くなり過ぎる…。

と言う事で、試しに以下のプログラムを動作させてみる。

----------------------------------------
#include<stdio.h>
#include<conio.h>
#include<windows.h>
#define _SIZE 256*256
char buffer[_SIZE*4*4];
void main( void )
{
    int    i;
    DWORD    start, end;

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*1);
    end        = GetTickCount( );
    printf( "256*256*1   : %5d\n", end - start );

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*2);
    end        = GetTickCount( );
    printf( "256*256*2   : %5d\n", end - start );

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*3);
    end        = GetTickCount( );
    printf( "256*256*3   : %5d\n", end - start );

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*4);
    end        = GetTickCount( );
    printf( "256*256*4   : %5d\n", end - start );

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*5);
    end        = GetTickCount( );
    printf( "256*256*5   : %5d\n", end - start );

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*6);
    end        = GetTickCount( );
    printf( "256*256*6   : %5d\n", end - start );

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*7);
    end        = GetTickCount( );
    printf( "256*256*7   : %5d\n", end - start );

    start    = GetTickCount( );
    for(i=0;i<1000;i++)
        memset(buffer,0,_SIZE*8);
    end        = GetTickCount( );
    printf( "256*256*8   : %5d\n", end - start );

    printf( "push any key.\n" );    _getch( );
}
----------------------------------------

結果は以下の通り…。

>256*256*1   :    10
>256*256*2   :    30
>256*256*3   :    50
>256*256*4   :    81
>256*256*5   :   480
>256*256*6   :   882
>256*256*7   :  1241
>256*256*8   :  1723
>push any key.

若干と言うかかなり誤差があると思うけど…
知りたい事が分かったのでよしとする!

「256*256*1~4」は、間隔が「20」ぐらいですが…
「256*256*5~8」は、間隔が「400」ぐらいになります…。

「256*256*4~5」を堺に20倍遅くなってる…!?

ちなみにこの遅くなるPCのCPUは、「Pentium III 1GHz」だ。

「Pentium III」のキャッシュって「256KB」…
それをバイトで書き直すと「262,144BYTE」…
って事は、丁度「=256*256*4BYTE」…
って事は、キャッシュに丁度収まったから高速…
って事らしい…。

キャッシュ恐るべし!

ちなみに、昔キャッシュの効力を確認するために
BIOSでキャッシュを全て切って起動すると
数十分たたないとたってもディスクトップに辿り着けません…。(汗)

キャッシュ恐るべし!

でも最近のCPUは、キャッシュが6MBとかあるので
およそ足りなくなることは無いかと…。

…多分…。

2008年12月15日------------------
ちょこっと修正。

| | コメント (0) | トラックバック (0)

より以前の記事一覧