Буфер связи, скорость, кошмар - PullRequest
3 голосов
/ 21 апреля 2011

Я пытаюсь использовать буферы для связи между несколькими «слоями» (потоками) в моей программе, и теперь, когда у меня есть визуальный вывод того, что происходит внутри, я понимаю, что в процессе поглощается огромное количество времени использования этих буферов.

Вот некоторые заметки о том, что происходит в моем коде.

  • когда режим рендеринга запускается в этом потоке, он начинает посылать как можно больше точек слою (потоку) под ним
  • точки из нижнего потока затем обрабатываются и возвращаются в этот поток через выходной буфер нижнего потока
  • полученные точки отображаются (пока) в виде белых пикселей на поверхности D3D

  • если я обойду буфер и поместлю точки непосредственно в пиксели поверхности, на выполнение всей работы уйдет всего около 3 секунд

  • если я передаю точку вниз, а затем нижний слой пропускает ее обратно вверх, пропуская любое фактическое сокращение числа, вся работа занимает около 30 минут (что делает всю программу бесполезной)
  • изменение размера моих буферов не оказывает заметного влияния на скорость

  • Первоначально я использовал MUTEX в своих буферах, но устранил их при попытке решить проблему

Есть ли что-то, что я могу сделать по-другому, чтобы исправить эту проблему со скоростью, которая у меня есть? ... как-то связано с тем, как я обрабатываю эти сообщения ???

Вот мой код Мне очень жаль, что это такой беспорядок. Мне нужно слишком быстро продвигаться в этом проекте, и я оставил много комментариев в комментариях, где я экспериментировал.

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;
}
}

(все еще привыкаешь к блокам кода на этом форуме)

Edit:

Вот пример, который я считаю похожим по концепции, хотя в этом примере используются сообщения, которые он создает в одной и той же цепочке.

#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 ]

2 голосов
/ 21 апреля 2011

Блокировка и разблокировка всего прямоугольника для изменения одной точки, вероятно, не очень эффективна, возможно, было бы лучше создать список точек, которые вы хотите изменить, а затем заблокировать прямоугольник один раз, повторяя этот список и изменяя все точкии затем разблокирование прямоугольника.

Когда вы блокируете прямоугольник, вы фактически блокируете параллельный доступ к нему, так что это как мьютекс для графического процессора в этом отношении - тогда вы изменяете только один пиксель.Повторение этого для каждого пикселя будет постоянно останавливать GPU.Вы могли бы использовать D3DLOCK_NOSYSLOCK, чтобы избежать этого в некоторой степени, но я не уверен, будет ли он хорошо играть в широком контексте вашей программы.

Я, очевидно, не совсем уверен, какова цель вашего алгоритма, но если вы пытаетесь параллельно обрабатывать пиксели на поверхности d3d, то я думаю, что наилучшим подходом будет использование шейдера на графическом процессоре.

Где вы в основном генерируете массив в системной памяти, заполняйте его "введите значения для каждой точки / пикселя, затем сгенерируйте текстуру на графическом процессоре из массива.Затем вы рисуете текстуру в полноэкранном квадре, а затем визуализируете ее с помощью пиксельного шейдера для какой-либо цели рендеринга.Шейдер может быть закодирован для обработки каждой точки любым удобным для вас способом, GPU позаботится об оптимизации распараллеливания.Затем вы генерируете новую текстуру из этой цели рендеринга и затем копируете эту текстуру в массив системной памяти.И тогда вы можете извлечь все свои выводы из этого массива.Вы также можете применить несколько шейдеров к результату рендеринга обратно в цель рендеринга, чтобы при необходимости преобразовать несколько преобразований.

1 голос
/ 21 апреля 2011

Пара замечаний:

  • Не пишите свой собственный код передачи мессапа.Это может быть правильно и медленно, или быстро и с ошибками.Требуется большой опыт для разработки быстрого кода, а затем сделать его без ошибок действительно трудным, потому что отладка многопоточного кода трудна.Win32 предоставляет несколько эффективных потоковобезопасных очередей: SList и оконную очередь сообщений.

  • Ваш дизайн разбивает работу наихудшим образом.Передача информации между потоками обходится дорого даже в самых лучших обстоятельствах, поскольку она приводит к конфликту в кэше как данных, так и объектов синхронизации.Гораздо лучше разделить вашу работу на отдельные не взаимодействующие (или минимизировать взаимодействие) наборы данных и передать каждый отдельный поток, который затем отвечает за все этапы обработки этого набора данных.

0 голосов
/ 25 апреля 2011

Мне удалось решить эту проблему, переработав все это

Теперь он передает огромные полезные нагрузки вместо отдельных задач

(я плакат)

0 голосов
/ 21 апреля 2011

Не опрашивайте.

Вероятно, это является причиной проблемы.У вас есть задача, постоянно звонящая peekmessage и, вероятно, ничего не находящая там.Это просто съест весь доступный процессор.Любая задача, которая хочет публиковать сообщения, вряд ли получит какое-либо процессорное время для достижения этой цели.

Я не могу вспомнить, как бы вы достигли этого с помощью очереди сообщений Windows (вероятно, WaitMessage или некоторый вариант), нообычно вы можете реализовать это с помощью счетного семафора.Когда потребитель хочет получить данные, он ожидает, чтобы семафор получил сигнал.Когда у производителя есть данные, он сигнализирует семафор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...