cheap/expensive内存DC在Win中如何API?
How cheap/expensive is memory DC in Win API?
我正在开发一个程序,该程序将包含许多 DIB 位图(由 CreateDIBSection
创建)并且必须使用 Win API.
在其上绘制大量文本
为了在位图上绘制,Windows 需要由 CreateCompatibleDC
创建的设备上下文。
现在有两种方法:
我可以为每个位图创建一次 DC,用于绘图并在释放位图时将其删除。
或者只有当我需要绘制到位图时才创建DC,调用绘制函数并删除DC。
更好的方法是什么?我更喜欢第一个,因为调用更少 - 这将使我的代码更小,也更快一点。
但是为每个位图保留一个长期存在的 DC 是不是太昂贵了?
Edit1:该应用程序实际上是一个 GUI 工具包库,将来可以以不同且不可预测的方式使用,因此我需要一个平衡的决定,即最大可能的性能和最小的系统资源使用。
GDI objects 是有限的,无论是每个进程还是每个会话。您正在与同一会话中的所有其他进程 运行 竞争资源。考虑到这一点,您应该仅在需要时才使用 GDI 资源(您问题中的选项 2)。
Mark Russinovich 的博客文章 Pushing the Limits of Windows: USER and GDI Objects – Part 2 介绍了相当多的细节。概括要点,这里是 window 管理器对 GDI 资源的限制列表:
- 每个进程 10.000 个 GDI 对象(默认值,可通过注册表项配置 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota)。
- 每个用户会话 65.535 个 GDI 对象。
- GDI 对象内存限制是页面缓冲池限制(参见 Pushing the Limits of Windows: Paged and Nonpaged Pool)。
这里有一个测试来检查调用 CreateCompatibleDC
需要多长时间。我发现每次调用平均需要 10 到 15 微秒。与 BitBlt
相比,这相对较快,特别是对于较大的图像。所以持有内存DC并没有多大优势。
case WM_PAINT:
{
static HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"path.bmp",
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
std::wostringstream oss;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
auto start = std::chrono::system_clock::now();
auto memdc = CreateCompatibleDC(hdc);
oss << L"CreateCompatibleDC: "
<< (std::chrono::system_clock::now() - start).count() / 10 << "\n";
auto oldbitmap = SelectObject(memdc, hbitmap);
start = std::chrono::system_clock::now();
BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, memdc, 0, 0, SRCCOPY);
oss << L"BitBlt: "
<< (std::chrono::system_clock::now() - start).count() / 10 << "\n";
SelectObject(memdc, oldbitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
OutputDebugString(oss.str().c_str());
break;
}
Windows10 的结果:
24 位 5MB 位图的结果:
CreateCompatibleDC
: 17 microseconds
BitBlt
: 2500 microseconds
8 位 275kb 的结果:
CreateCompatibleDC
: 12 microseconds
BitBlt
: 500 microseconds
我正在开发一个程序,该程序将包含许多 DIB 位图(由 CreateDIBSection
创建)并且必须使用 Win API.
为了在位图上绘制,Windows 需要由 CreateCompatibleDC
创建的设备上下文。
现在有两种方法:
我可以为每个位图创建一次 DC,用于绘图并在释放位图时将其删除。
或者只有当我需要绘制到位图时才创建DC,调用绘制函数并删除DC。
更好的方法是什么?我更喜欢第一个,因为调用更少 - 这将使我的代码更小,也更快一点。
但是为每个位图保留一个长期存在的 DC 是不是太昂贵了?
Edit1:该应用程序实际上是一个 GUI 工具包库,将来可以以不同且不可预测的方式使用,因此我需要一个平衡的决定,即最大可能的性能和最小的系统资源使用。
GDI objects 是有限的,无论是每个进程还是每个会话。您正在与同一会话中的所有其他进程 运行 竞争资源。考虑到这一点,您应该仅在需要时才使用 GDI 资源(您问题中的选项 2)。
Mark Russinovich 的博客文章 Pushing the Limits of Windows: USER and GDI Objects – Part 2 介绍了相当多的细节。概括要点,这里是 window 管理器对 GDI 资源的限制列表:
- 每个进程 10.000 个 GDI 对象(默认值,可通过注册表项配置 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota)。
- 每个用户会话 65.535 个 GDI 对象。
- GDI 对象内存限制是页面缓冲池限制(参见 Pushing the Limits of Windows: Paged and Nonpaged Pool)。
这里有一个测试来检查调用 CreateCompatibleDC
需要多长时间。我发现每次调用平均需要 10 到 15 微秒。与 BitBlt
相比,这相对较快,特别是对于较大的图像。所以持有内存DC并没有多大优势。
case WM_PAINT:
{
static HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"path.bmp",
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
std::wostringstream oss;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
auto start = std::chrono::system_clock::now();
auto memdc = CreateCompatibleDC(hdc);
oss << L"CreateCompatibleDC: "
<< (std::chrono::system_clock::now() - start).count() / 10 << "\n";
auto oldbitmap = SelectObject(memdc, hbitmap);
start = std::chrono::system_clock::now();
BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, memdc, 0, 0, SRCCOPY);
oss << L"BitBlt: "
<< (std::chrono::system_clock::now() - start).count() / 10 << "\n";
SelectObject(memdc, oldbitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
OutputDebugString(oss.str().c_str());
break;
}
Windows10 的结果:
24 位 5MB 位图的结果:
CreateCompatibleDC
: 17 microseconds
BitBlt
: 2500 microseconds
8 位 275kb 的结果:
CreateCompatibleDC
: 12 microseconds
BitBlt
: 500 microseconds