我喜欢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;
}
为什么这个操作会随着时间的推移而减慢?
答案 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
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;
setpixel
的工作方式开始(可能正常工作,但我会说它的工作方式)。第一个也是最重要的任务是参数验证。谷歌参与验证(或stackexchange)的成本。它还包括将屏幕数据映射到设备上下文,然后测试传递的坐标。简单地说,需要考虑时间。 SetPixel()
是gdi32.dll中的函数,而BOOM,图形子系统驻留在内核模式!!因此,对于每个像素和每个像素都存在Usermode -> KernelMode -> Usermode
的开销,这就是为什么第1点和第2点比通常情况更重要的原因。现在很容易猜到为什么它会随着时间的推移而减速。GetEnhMetaFile
SetPixel()
SetPixel()
醇>
更重要的是 - &gt;您应该知道何时使用{{1}}。如果你使用它来做可以使用其他耗时较少的机制完成的事情,那就是你的错误,而不是MS。您应该将它用于手写分析或类似的目的,其中单个或非常少的像素值会产生差异
你绝对可以谷歌通过连续切换模式大约100000次来表达它是什么意思。