SetPixel会随着时间的推移而变慢

时间:2014-01-15 17:04:32

标签: c winapi time pixels

我喜欢SetPixel上的DesktopWindow,但有时表现得很奇怪。

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

上面的代码应打印10,000像素,在屏幕的左上角绘制一个圆圈。但如果我多次使用它,它会变得越来越慢。下面的代码应该提供一个例子:

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

为什么这个操作会随着时间的推移而减慢?

2 个答案:

答案 0 :(得分:2)

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

#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吗?

Yessir,我抓住了罪行的罪魁祸首。

这些测试最好用于打开任务管理器,以便在工作中观看可怕的dwm.exe桌面窗口(Inept)管理器。

在第一次执行期间,dwm.exe被卡在100%CPU(使用我的PC的4个核心之一)并且其内存消耗量上升到可笑的数量(从大约28 Mb到112 Mb)。

即使暂停20秒,血腥的DWM也没有完成消化像素。这就是测试的第二部分显示执行时间稍长的原因。

如果没有Aero,SetPixel函数基本上什么都不做。 DC无效,但SetPixel不执行任何(可见)修改。

到底是什么?

所有这一切发生的可能原因是,使用华而不实的“新”(自Vista之后)桌面界面,最终桌面位图的组合由此dwm.exe进程完成。每个窗口都有自己的图形缓冲区,dwm.exe会收到任何更改通知,并重新计算后台每个像素的最终方面。

将像素直接写入桌面窗口基本上搞砸了那个小方案,因为外部程序访问了dwm.exe的私人游乐场。

我不知道微软的家伙是如何处理这种情况的,但显然他们并没有采用任何有效的方式来做这件事。
看起来桌面的多个副本被加载到内存中,可能允许修改一个副本而其他副本集成在合成链中。

dwm.exe吞噬的内存量大约是1000 * 1000 RGBA位图大小的25倍。
测试显示该数量随修改区域的表面而变化。

我怀疑愚蠢的过程每秒对屏幕进行20或30次采样,并在发现奇怪的事情(例如SetPixel调用)时创建屏幕修改部分的新副本。

有了这么糟糕的结果,我想知道为什么他们首先允许访问桌面DC,除了允许人们用10行代码涂抹屏幕。

现在,高效的屏幕访问需要使用DirectX来绕过可靠的后向兼容层,你必须通过过时的Win32 GDI来操作位图。

答案 1 :(得分:2)

嗯,Kuroi neko试图在某种程度上解释它,但我认为确切原因没有得到解答。

为什么它会随着时间的推移而减速?

==&GT;因为最终结果是nlogn

如何==&gt;

  1. 让我们从setpixel的工作方式开始(可能正常工作,但我会说它的工作方式)。第一个也是最重要的任务是参数验证。谷歌参与验证(或stackexchange)的成本。它还包括将屏幕数据映射到设备上下文,然后测试传递的坐标。简单地说,需要考虑时间。
  2. Pixel有一些属性;所以它必须填写一些结构有点东西,然后它必须创建调色板,然后填写颜色位acc。到结构,然后它需要将此调色板映射到设备上下文。接下来,它需要清理这个像素的混乱,几乎相似(可能少一点)的工作是在商店中为下一个像素
  3. 现在真正的肉:SetPixel()是gdi32.dll中的函数,而BOOM,图形子系统驻留在内核模式!!因此,对于每个像素和每个像素都存在Usermode -> KernelMode -> Usermode的开销,这就是为什么第1点和第2点比通常情况更重要的原因。现在很容易猜到为什么它会随着时间的推移而减速。
  4. MS是否试图解决此问题?

    1. 是,是的,事实上这是因为解决其他一些问题而引起的问题。简单地说,图形子系统在初始设计中不是内核模式的一部分,但是提高性能他们将其移动到内核模式。
    2. 他们尝试了GetEnhMetaFile SetPixel()
    3. 内的SetPixel()

      更重要的是 - &gt;您应该知道何时使用{{1}}。如果你使用它来做可以使用其他耗时较少的机制完成的事情,那就是你的错误,而不是MS。您应该将它用于手写分析或类似的目的,其中单个或非常少的像素值会产生差异

      你绝对可以谷歌通过连续切换模式大约100000次来表达它是什么意思。