我喜欢在SetPixelDesktopWindow,但有时它的行为很奇怪。

for(i=0;i<10000;i++)
    SetPixel(DC,100+100*sin((float)i/100),100+100*cos((float)i/100),0);

上面的代码应该打印10000像素,在屏幕左上角画一个圆圈。但如果我多次使用它,它会变得越来越慢。下面的代码应该提供一个例子:
#include<windows.h>
int main(){
    Sleep(4000);//waiting you to be ready
    int i,j,k,l;
    HDC DC=GetDC(GetDesktopWindow());
    j=GetTickCount();//base time
    for(l=0;l<10;l++)
    {
        for(i=0;i<10000;i++)
            SetPixel(DC,rand()%1000,rand()%1000,0);//print 10000 random x,y pixel
        printf("%d\n",(k=GetTickCount())-j);//time duration from the last count
        for(i=0;i<10000;i++)
            SetPixel(DC,rand()%1000,rand()%1000,0);
        printf("%d\n",(j=GetTickCount())-k);
    }
    return 0;
}

为什么这次行动会随着时间的推移而放缓?

最佳答案

首先,对测试代码进行一些清理:

#include<windows.h>

// number of pixels written in each run
#define NUM_PIXELS 50000

// range of pixel coordinates
#define MIN_RANGE 100
#define MAX_RANGE 1000
#define RANGE_MULT 10

// pause after each run to allow DWM to do its things
#define DWM_PAUSE 20 // seconds

HDC DC;

void bench(int range, int pause)
{
    int i, start;

    // let DWM digest previous pixels
    Sleep(pause*1000);

    // feed more pixels into the system
    start = GetTickCount();
    for (i = 0; i != NUM_PIXELS; i++)
    {
        SetPixel(DC, rand()%range, rand()%range, 0);
    }
    printf ("pause %d range %d duration %d\n", pause, range, GetTickCount()-start);
}

int main (void)
{
    DC=GetDC(GetDesktopWindow());

    int range;
    for (range = MIN_RANGE; range <= MAX_RANGE; range *= RANGE_MULT) bench(range, 0);
    for (range = MAX_RANGE; range >= MIN_RANGE; range /= RANGE_MULT) bench(range, 0);
    for (range = MIN_RANGE; range <= MAX_RANGE; range *= RANGE_MULT) bench(range, DWM_PAUSE);
    for (range = MAX_RANGE; range >= MIN_RANGE; range /= RANGE_MULT) bench(range, DWM_PAUSE);
    return 0;
}

在启用Aero桌面的Win7上运行此程序产生了以下结果:
c:\Dev\PHP\_StackOverflow\C++\SlowSetPixel\Release>SlowSetPixel.exe
pause 0 range 100 duration 1404
pause 0 range 1000 duration 5273
pause 0 range 1000 duration 8377
pause 0 range 100 duration 3713
pause 20 range 100 duration 3089
pause 20 range 1000 duration 6942
pause 20 range 1000 duration 8455
pause 20 range 100 duration 3151

在禁用Aero的情况下运行的同一程序:
c:\Dev\PHP\_StackOverflow\C++\SlowSetPixel\Release>SlowSetPixel.exe
pause 0 range 100 duration 47
pause 0 range 1000 duration 31
pause 0 range 1000 duration 31
pause 0 range 100 duration 31
pause 20 range 100 duration 63
pause 20 range 1000 duration 47
pause 20 range 1000 duration 47
pause 20 range 100 duration 62

有人偷了我的CPU吗?
是的,我当场抓住了罪犯。
这些测试最好与打开的任务管理器一起使用,以查看工作中可怕的桌面窗口(Inept)管理器。
在第一次执行期间,dwm.exe被卡在100%的CPU(使用我的PC的4个核心之一)上,其内存消耗量上升到可笑的数量(从大约28mb增加到112mb)。
即使暂停20秒,血腥的DWM甚至没有完成像素的消化。这就是为什么测试的第二部分显示稍长的执行时间。
如果没有Aero,SetPixel函数基本上什么都不做。DC不是无效的,但是dwm.exe不执行任何(可见的)修改。
搞什么鬼?
这一切发生的可能原因是,使用华而不实的“新”(自Vista以来)桌面界面,最终桌面位图的合成是通过这个SetPixel过程完成的。每个窗口都有自己的图形缓冲区,dwm.exe会收到任何更改的通知,并重新计算背景中每个像素的最终方面。
将像素直接写入桌面窗口基本上会搞砸这个小方案,因为外部程序访问的是所谓的dwm.exe的私人游乐场。
我不知道微软是怎么处理这件事的,但很明显他们并没有以一种有效的方式来处理这件事。
看起来桌面的多个副本被加载到内存中,可能是为了允许一个副本被修改,而其他副本被集成到合成链中。
dwm.exe占用的内存量大约是1000*1000 RGBA位图大小的25倍。
试验表明,该量随改性区域的表面而变化。
我怀疑这个愚蠢的过程每秒对屏幕进行20或30次采样,当它看到发生了一些奇怪的事情(比如dwm.exe调用)时,会创建屏幕修改部分的新副本。
这么糟糕的结果,我想知道为什么他们一开始就允许访问桌面DC,除了允许人们用10行代码涂抹屏幕。
高效的屏幕访问现在需要使用DirectX来绕过可怕的向后兼容层,你必须通过这个层才能使用过时的Win32 GDI操作位图。

关于c - SetPixel随着时间的流逝而变慢,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21143711/

10-13 04:23