缓冲通信速度噩梦

时间:2011-04-21 01:29:38

标签: c++ multithreading performance buffer layer

我正在尝试使用缓冲区在我的程序中的几个“层”(线程)之间进行通信,现在我已经看到内部发生了什么,我意识到在这个过程中有大量的时间被吃掉了使用这些缓冲区。

以下是关于我的代码中发生了什么的一些注释。

  • 当在这个线程中触发渲染模式时,它开始向它下面的层(线程)发送尽可能多的点
  • 然后处理来自底线的点并通过底线的输出缓冲区
  • 返回到该线程
  • 收到的点被映射(现在)为D3D表面的白色像素

  • 如果我绕过缓冲区并将点直接放入表面像素,那么整个工作只需要3秒钟

  • 如果我把点向下移动,然后让下层向右传递,跳过任何实际的数字运算,整个工作大约需要30分钟(这会使整个程序无用)
  • 更改缓冲区的大小对速度没有明显影响

  • 我最初在我的缓冲区中使用MUTEXes,但已尝试将其删除以解决问题

我有什么不同的方法可以解决我遇到的速度问题吗? ......与处理这些消息的方式有关?

这是我的代码 我很抱歉这是一团糟。我不得不在这个项目上走得太快,我在试验中留下了很多文章。

DWORD WINAPI CONTROLSUBSYSTEM::InternalExProcedure(__in LPVOID lpSelf)
{
    XMSG xmsg;
    LPCONTROLSUBSYSTEM lpThis = ((LPCONTROLSUBSYSTEM)lpSelf);
    BOOL bStall;

    BOOL   bRendering = FALSE;
    UINT64 iOutstandingPoints = 0; // points that are out being tested
    UINT64 iPointsDone = 0;
    UINT64 iPointsTotal = 0;

    BOOL bAssigning;
    DOUBLE dNextX;
    DOUBLE dNextY;

    while(1)
    {
        if( lpThis->hwTargetWindow!=NULL && lpThis->d3ddev!=NULL )
        {
            lpThis->d3ddev->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
            if(lpThis->d3ddev->BeginScene())
            {
                lpThis->d3ddev->StretchRect(lpThis->sfRenderingCanvas,NULL,lpThis->sfBackBuffer,NULL,D3DTEXF_NONE);
                lpThis->d3ddev->EndScene();
            }
            lpThis->d3ddev->Present(NULL,NULL,NULL,NULL);
        }

        //bStall = TRUE;
        // read input buffer
        if(lpThis->bfInBuffer.PeekMessage(&xmsg))
        {
            bStall = FALSE;

            if( HIBYTE(xmsg.wType)==HIBYTE(CONT_MSG) )
            {
                // take message off
                lpThis->bfInBuffer.GetMessage(&xmsg);
                // double check consistency
                if( HIBYTE(xmsg.wType)==HIBYTE(CONT_MSG) )
                {
                    switch(LOBYTE(xmsg.wType))
                    {
                    case SETRESOLUTION_MSG:
                        lpThis->iAreaWidth = (UINT)xmsg.dptPoint.X;
                        lpThis->iAreaHeight = (UINT)xmsg.dptPoint.Y;
                        lpThis->sfRenderingCanvas->Release();
                        if(lpThis->d3ddev->CreateOffscreenPlainSurface(
                            (UINT)xmsg.dptPoint.X,(UINT)xmsg.dptPoint.Y,
                            D3DFMT_X8R8G8B8,
                            D3DPOOL_DEFAULT,
                            &(lpThis->sfRenderingCanvas),
                            NULL)!=D3D_OK)
                        {
                            MessageBox(NULL,"Error resizing surface.","ERROR",MB_ICONERROR);
                        }
                        else
                        {
                            D3DLOCKED_RECT lrt;
                            if(D3D_OK == lpThis->sfRenderingCanvas->LockRect(&lrt,NULL,0))
                            {
                                lpThis->iPitch = lrt.Pitch;
                                VOID *data;
                                data = lrt.pBits;
                                ZeroMemory(data,lpThis->iPitch*lpThis->iAreaHeight);
                                lpThis->sfRenderingCanvas->UnlockRect();

                                MessageBox(NULL,"Surface Resized","yay",0);
                            }
                            else
                            {
                                MessageBox(NULL,"Error resizing surface.","ERROR",MB_ICONERROR);
                            }
                        }
                        break;
                    case SETCOLORMETHOD_MSG:
                        break;
                    case SAVESNAPSHOT_MSG:
                        lpThis->SaveSnapshot();
                        break;
                    case FORCERENDER_MSG:
                        bRendering = TRUE;
                        iPointsTotal = lpThis->iAreaHeight*lpThis->iPitch;
                        iPointsDone = 0;

                        MessageBox(NULL,"yay, render something!",":o",0);
                        break;
                    default:
                        break;
                    }
                }// else, lost this message
            }
            else
            {
                if( HIBYTE(xmsg.wType)==HIBYTE(MATH_MSG) )
                {
                    XMSG xmsg2;
                    switch(LOBYTE(xmsg.wType))
                    {
                    case RESETFRAME_MSG:
                    case ZOOMIN_MSG:
                    case ZOOMOUT_MSG:
                    case PANUP_MSG:
                    case PANDOWN_MSG:
                    case PANLEFT_MSG:
                    case PANRIGHT_MSG:
                        // tell self to start a render
                        xmsg2.wType = CONT_MSG|FORCERENDER_MSG;
                        if(lpThis->bfInBuffer.PutMessage(&xmsg2))
                        {
                            // pass it down
                            while(!lpThis->lplrSubordinate->PutMessage(&xmsg));
                            // message passed so pull it from buffer
                            lpThis->bfInBuffer.GetMessage(&xmsg);
                        }
                        break;
                    default:
                        // pass it down
                        if(lpThis->lplrSubordinate->PutMessage(&xmsg))
                        {
                            // message passed so pull it from buffer
                            lpThis->bfInBuffer.GetMessage(&xmsg);
                        }
                        break;
                    }
                }
                else if( lpThis->lplrSubordinate!=NULL )
                // pass message down
                {
                    if(lpThis->lplrSubordinate->PutMessage(&xmsg))
                    {
                        // message passed so pull it from buffer
                        lpThis->bfInBuffer.GetMessage(&xmsg);
                    }
                }
            }
        }
        // read output buffer from subordinate
        if( lpThis->lplrSubordinate!=NULL && lpThis->lplrSubordinate->PeekMessage(&xmsg) )
        {
            bStall = FALSE;
            if( xmsg.wType==(REPLY_MSG|TESTPOINT_MSG) )
            {
                // got point test back
                D3DLOCKED_RECT lrt;
                if(D3D_OK == lpThis->sfRenderingCanvas->LockRect(&lrt,NULL,0))
                {
                    INT pitch = lrt.Pitch;
                    VOID *data;
                    data = lrt.pBits;
                    INT Y=dRound((xmsg.dptPoint.Y/(DOUBLE)100)*((DOUBLE)lpThis->iAreaHeight));
                    INT X=dRound((xmsg.dptPoint.X/(DOUBLE)100)*((DOUBLE)pitch));

                    // decide color
                    if( xmsg.iNum==0 )
                        ((WORD *)data)[X+Y*pitch] = 0xFFFFFFFF;
                    else
                        ((WORD *)data)[X+Y*pitch] = 0xFFFFFFFF;

                    // message handled so remove from buffer
                    lpThis->lplrSubordinate->GetMessage(&xmsg);

                    lpThis->sfRenderingCanvas->UnlockRect();
                }
            }
            else if(lpThis->bfOutBuffer.PutMessage(&xmsg))
            {
                // message sent so pull the real one off the buffer
                lpThis->lplrSubordinate->GetMessage(&xmsg);
            }
        }
        if( bRendering && lpThis->lplrSubordinate!=NULL )
        {
            bAssigning = TRUE;
            while(bAssigning)
            {
                dNextX = 100*((DOUBLE)(iPointsDone%lpThis->iPitch))/((DOUBLE)lpThis->iPitch);
                dNextY = 100*(DOUBLE)((INT)(iPointsDone/lpThis->iPitch))/(DOUBLE)(lpThis->iAreaHeight);
                xmsg.dptPoint.X = dNextX;
                xmsg.dptPoint.Y = dNextY;
                //
                //xmsg.iNum = 0;
                //xmsg.wType = REPLY_MSG|TESTPOINT_MSG;
                //
                xmsg.wType = MATH_MSG|TESTPOINT_MSG;

                /*D3DLOCKED_RECT lrt;
                if(D3D_OK == lpThis->sfRenderingCanvas->LockRect(&lrt,NULL,0))
                {
                    INT pitch = lrt.Pitch;
                    VOID *data;
                    data = lrt.pBits;
                    INT Y=dRound((dNextY/(DOUBLE)100)*((DOUBLE)lpThis->iAreaHeight));
                    INT X=dRound((dNextX/(DOUBLE)100)*((DOUBLE)pitch));
                    ((WORD *)data)[X+Y*pitch] = 0xFFFFFFFF;
                    lpThis->sfRenderingCanvas->UnlockRect();
                }
                iPointsDone++;
                if( iPointsDone>=iPointsTotal )
                {
                    MessageBox(NULL,"done rendering","",0);
                    bRendering = FALSE;
                    bAssigning = FALSE;
                }
                */
                if( lpThis->lplrSubordinate->PutMessage(&xmsg) )
                {
                    bStall = FALSE;
                    iPointsDone++;
                    if( iPointsDone>=iPointsTotal )
                    {
                        MessageBox(NULL,"done rendering","",0);
                        bRendering = FALSE;
                        bAssigning = FALSE;
                    }
                }
                else
                {
                    bAssigning = FALSE;
                }
            }
        }

        //if( bStall )
            //Sleep(10);
    }

    return 0;
}
}

(仍然习惯了这个论坛的代码块)

编辑:

这是一个我认为在概念上相似的例子,尽管这个例子消耗了它在同一个线程中产生的消息。

#include <Windows.h>
#include "BUFFER.h"

int main()
{
BUFFER myBuffer;

INT jobsTotal = 1024*768;
INT currentJob = 0;
INT jobsOut = 0;
XMSG xmsg;

while(1)
{
    if(myBuffer.PeekMessage(&xmsg))
    {
        // do something with message
        // ...

        // if successful, remove message
        myBuffer.GetMessage(&xmsg);
        jobsOut--;
    }

    while( currentJob<jobsTotal )
    {
        if( myBuffer.PutMessage(&xmsg) )
        {
            currentJob++;
            jobsOut++;
        }
        else
        {
            // buffer is full at the moment
            // stop for now and put more on later
            break;
        }
    }

    if( currentJob==jobsTotal && jobsOut==0 )
    {
        MessageBox(NULL,"done","",0);
        break;
    }
}

return 0;
}

此示例也在大约3秒内运行,而不是30分钟。

不过,如果有人知道为什么visual studio会一直试图让我说PeekMessageA和GetMessageA而不是我定义的实际名字,那也很高兴知道。

4 个答案:

答案 0 :(得分:2)

锁定和解锁整个矩形以更改单个点可能效率不高,您可能最好生成一个要修改的点列表,然后锁定一次矩形,迭代该列表并修改所有点,然后解锁矩形。

当你锁定矩形时,你实际上停止了对它的并发访问,所以它就像GPU中的互斥体一样 - 那么你只修改一个像素。对每个像素重复执行此操作将不断停止GPU。您可以在某种程度上使用D3DLOCK_NOSYSLOCK来避免这种情况,但我不确定它是否会在您的程序的更大上下文中很好地运行。

我显然不完全确定你的算法的目标是什么,但是如果你想在d3d表面上并行处理像素,那么我认为最好的方法是通过GPU上的着色器。

如果您基本上在系统内存中生成一个数组,则在每个点/像素的基础上使用“输入”值填充它,然后在阵列上的GPU上生成纹理。接下来,将纹理绘制为全屏四边形,然后使用像素着色器将其渲染到某个渲染目标。可以对着色器进行编码以便以您喜欢的任何方式处理每个点,GPU将负责优化并行化。然后从该渲染目标生成一个新纹理,然后将该纹理复制到系统内存数组中。然后,您可以从该阵列中提取所有输出。您还可以将渲染目标结果中的多个着色器应用回渲染目标,以根据需要管理多个转换。

答案 1 :(得分:1)

一对夫妇注意到:

  • 不要编写自己的密码传递代码。它可能是正确和缓慢的,或快速和马车。设计快速的代码需要很多经验然后让它没有错误是非常困难的,因为调试线程代码很难。 Win32提供了几个高效的线程安全队列:SList和窗口消息队列。

  • 您的设计以最糟糕的方式分解工作。即使在最好的情况下,在线程之间传递信息也是昂贵的,因为它会导致数据和同步对象上的高速缓存争用。最好将您的工作分成不同的非交互(或最小化交互)数据集,并将每个数据集分配给一个单独的线程,然后负责处理该数据集的所有阶段。

答案 2 :(得分:0)

不要投票。

这可能是问题的核心。你有一个不断调用peekmessage的任务,可能在那里找不到任何东西。这将只吃掉所有可用的CPU。任何想要发布消息的任务都不可能获得任何CPU时间来实现这一点。

我不记得你是如何使用Windows消息队列(可能是WaitMessage或某些变体)实现这一点的,但通常你可以使用计数信号量来实现它。当消费者想要数据时,它等待信号量发出信号。当生产者有数据时,它会发出信号信号。

答案 3 :(得分:0)

我设法通过重新设计整个事情来解决它

它现在传递巨大的有效负载而不是单个任务

(我是海报)