缓冲通信速度噩梦
Posted
技术标签:
【中文标题】缓冲通信速度噩梦【英文标题】:Buffer communication speed nightmare 【发布时间】:2011-04-21 01:29:38 【问题描述】:我正在尝试使用缓冲区在我的程序中的几个“层”(线程)之间进行通信,现在我可以看到内部发生的事情,我意识到在这个过程中消耗了大量的时间使用这些缓冲区。
这里有一些关于我的代码发生了什么的注释。
当在这个线程中触发渲染模式时,它开始向它下面的层(线程)发送尽可能多的点 来自下层线程的点然后被处理并通过下层线程的输出缓冲区返回到该线程接收回来的点(目前)被映射为 D3D 表面中的白色像素
如果我绕过缓冲区,将点直接放入表面像素中,完成整个工作只需要大约 3 秒
如果我将要点传下来,然后让下层直接将其传递回去,跳过任何实际的数字运算,整个工作大约需要 30 分钟(这使得整个程序毫无用处)更改缓冲区大小对速度没有明显影响
我最初在缓冲区中使用 MUTEX,但为了解决问题而将其删除
我有什么办法可以解决我遇到的这个速度问题吗? ...与我处理这些消息的方式有关???
这是我的代码 我很抱歉,这是一团糟。我不得不在这个项目上走得太快,而且我在我一直在试验的 cmets 中留下了很多碎片。
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 而不是我定义的实际名称,那也很高兴知道。
【问题讨论】:
你知道,我真的很想帮忙,但你已经有大量的代码,这有点复杂和无关紧要,而且还不完整。如果您可以将其提炼成一个显示该问题的非常小的应用程序,您会得到更好的响应。PeekMessageA
等的原因是您已包含 windows.h
标头,该标头将 PeekMessage
定义为 PeekMessageA
或 PeekMessageW
的宏,具体取决于您的 unicode设置。您可以 (1) 使用它,(2) 使用不与 Win32 API 重叠的不同名称,(3) 将必须包含 windows.h 的 Win32 代码与缓冲区代码分开,并且不包含 windows.h在使用缓冲区的同一文件中,或 (4) #undef
这些宏,并在您确实需要 Win32 函数时使用完整名称 ::PeekMessageA
。
【参考方案1】:
锁定和解锁整个矩形以更改单个点可能不是很有效,您最好生成一个要修改的点列表,然后锁定一次矩形,遍历该列表并修改所有点,然后解锁矩形。
当您锁定 rect 时,您实际上是在停止对它的并发访问,因此在这方面它就像 GPU 的互斥锁 - 然后您只需修改单个像素。对每个像素重复执行此操作将不断使 GPU 停顿。您可以使用 D3DLOCK_NOSYSLOCK 在某种程度上避免这种情况,但我不确定它是否会在您的程序的更大上下文中很好地发挥作用。
我显然不完全确定您算法的目标是什么,但如果您尝试在 d3d 表面上并行处理像素,那么我认为最好的方法是通过 GPU 上的着色器。
您基本上在系统内存中生成一个数组,在每个点/像素的基础上用“输入”值填充它,然后从数组在 GPU 上生成纹理。接下来,您将纹理绘制到全屏四边形,然后使用像素着色器将其渲染到某个渲染目标。可以对着色器进行编码,以您喜欢的任何方式处理每个点,GPU 将负责优化并行化。然后从该渲染目标生成一个新纹理,然后将该纹理复制到系统内存数组中。然后您可以从该数组中提取所有输出。如果需要,您还可以将多个着色器应用到渲染目标结果返回到渲染目标,以通过管道传输多个转换。
【讨论】:
我尝试注释掉锁定矩形并编辑表面的部分,因为我也想知道这是否有帮助。它对速度根本没有明显影响。【参考方案2】:几个注意事项:
不要编写自己的消息传递代码。它可能是正确的和缓慢的,或者是快速的和错误的。设计快速的代码然后让它没有错误真的很难,因为调试线程代码很难。 Win32 提供了几个高效的线程安全队列:SList
和窗口消息队列。
您的设计以最糟糕的方式拆分工作。即使在最好的情况下,在线程之间传递信息也是昂贵的,因为它会导致数据和同步对象的缓存争用。最好将您的工作分成不同的非交互(或最小化交互)数据集,并将每个数据集分配给一个单独的线程,然后由该线程负责处理该数据集的所有阶段。
【讨论】:
【参考方案3】:不要投票。
这很可能是问题的核心。你有一个任务不断地调用peekmessage
并且可能在那里找不到任何东西。这只会吃掉所有可用的 CPU。任何想要发布消息的任务都不太可能获得任何 CPU 时间来实现这一点。
我不记得您是如何使用 Windows 消息队列(可能是 WaitMessage
或其他变体)实现这一点的,但通常您可以使用计数信号量来实现这一点。当消费者需要数据时,它会等待信号量发出信号。当生产者有数据时,它会向信号量发出信号。
【讨论】:
【参考方案4】:我设法通过重新设计整个事情来解决它
它现在传递大量有效负载而不是单个任务
(我是海报)
【讨论】:
以上是关于缓冲通信速度噩梦的主要内容,如果未能解决你的问题,请参考以下文章