2

假设我需要实现一个 out-proc COM 服务器并且所有的 COM 接口都是自动化兼容的。我可以创建和注册代理/存根组合,也可以创建和注册类型库并依赖自动化编组器。

我很清楚这两种方法的所有维护方面。这个问题仅与运行时性能有关。

我找不到任何硬数据——只有像这样的声明

  1. “自动化编组器是通用的,所以它更慢”我不会马上相信,因为只有几种自动化兼容类型,所以在它们之间切换并不难

  2. “自动化编组器将必须加载类型库”这是公平的,但这只需执行一次,如果我之后有十万个 COM 调用,我不太关心一次性开销

从长远来看,是否有任何测量数据(代理/存根编组或类型库编组)更快?

4

1 回答 1

1

根据 Don Box 在Essential Com (p. 228) 和他的 Microsoft Systems Journal 文章 (Jan 1999)中的说法,一旦创建了代理,PSOAInterface 的性能应该与 /Oicf 代理/存根库的性能相同。

但从 Windows 8.1 开始,PSOAInterface 代理的创建可能不是最理想的。Don Box 在上面的文章中声称combase!CreateProxyFromTypeInfo 和类型库封送器执行缓存。但是,在我的测试中,每次发布所有接口后,都会从文件中重新加载类型库。这是 UIAutomation 库的一个重大问题,它广泛使用 IGlobalInterfaceTable,导致大量额外的系统调用。

这是我的测试。一个公寓实例化一个 coclass,然后另一个公寓将其解组为代理 10000 次。使用 PSOAInterface 大约需要 5s。如果我改为创建和注册 MIDL 代理/存根,只需要大约 100 毫秒。

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <comdef.h>

// RemoteProxyFactory32 from oleacc.dll {53362c64-a296-4f2d-a2f8-fd984d08340b}
static IID CLSID_RemoteProxyFactory32 = GUID{ 0x53362c64, 0xa296, 0x4f2d, { 0xa2, 0xf8, 0xfd, 0x98, 0x4d, 0x08, 0x34, 0x0b } };
// IRemoteProxyFactory from oleacc.dll {8628f27d-64a2-4ed6-906b-e6155314c16a}
static IID IID_IRemoteProxyFactory = GUID{ 0x8628f27d, 0x64a2, 0x4ed6, { 0x90, 0x6b, 0xe6, 0x15, 0x53, 0x14, 0xc1, 0x6a } };

struct register_interface_thread {
    HANDLE hInterfaceRegistered;
    DWORD dwCookie;
    HANDLE hShouldClose;
};
DWORD WINAPI RegisterInterfaceThread(_In_  LPVOID lpParameter) {
    struct register_interface_thread& state = *(struct register_interface_thread*)(lpParameter);
    HRESULT hr;
    if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED))) {
        fprintf(stderr, "Error CoInitializeEx: 0x%08x\n", hr);
    }
    else {
        IUnknown *pv;
        if (FAILED(hr = CoCreateInstance(CLSID_RemoteProxyFactory32, NULL, CLSCTX_LOCAL_SERVER, IID_IRemoteProxyFactory, (LPVOID*)&pv))) {
            fprintf(stderr, "CocCreateInstance(RemoteProxyFactory32 of oleacc.dll) failed with hresult 0x%x\n", hr);
        }
        else {
            IGlobalInterfaceTable *pIGlobalInterfaceTable;
            if (FAILED(hr = CoCreateInstance
                (
                CLSID_StdGlobalInterfaceTable,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_IGlobalInterfaceTable,
                (void **)&pIGlobalInterfaceTable
                ))) {
                fprintf(stderr, "CocCreateInstance(StdGlobalInterfaceTable) failed with hresult 0x%x\n", hr);
            }
            else {
                DWORD dwCookie;
                if (FAILED(hr = pIGlobalInterfaceTable->RegisterInterfaceInGlobal(pv, IID_IRemoteProxyFactory, &dwCookie))) {
                    fprintf(stderr, "RegisterInterfaceInGlobal failed with hresult 0x%x\n", hr);
                }
                else {
                    fprintf(stdout, "Successfully registered interface; cookie=0x%x\n", dwCookie);
                    state.dwCookie = dwCookie;
                    if (!SetEvent(state.hInterfaceRegistered)) {
                        DWORD err = GetLastError();
                        fprintf(stderr, "Error SetEvent(hInterfaceRegistered): 0x%x\n", err);
                    }
                    else {
                        DWORD waitResult;
                        if (WAIT_OBJECT_0 != (waitResult = WaitForSingleObject(state.hShouldClose, INFINITE))) {
                            DWORD err = GetLastError();
                            fprintf(stderr, "Error WaitForSingleObject: returned 0x%x; error=0x%08x\n", waitResult, err);
                            hr = err;
                        }
                        else {
                            fprintf(stdout, "Successfully joined thread; dwCookie=0x%x\n", state.dwCookie);
                        }
                    }
                }
                pIGlobalInterfaceTable->Release();
            }
            if (pv != NULL)
                pv->Release();
        }
        CoUninitialize();
        fprintf(stdout, "Thread going away\n");
    }
    return 0;
}

int main(int argc, char* argv[]) {
    HRESULT hr;
    if (FAILED(hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
        fprintf(stderr, "Error CoInitializeEx: 0x%08x\n", hr);
    }
    else {
        struct register_interface_thread state;
        state.dwCookie = 0;
        state.hInterfaceRegistered = CreateEventEx(NULL, TEXT("hInterfaceRegistered"), CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
        state.hShouldClose = CreateEventEx(NULL, TEXT("hShouldClose"), CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);

        HANDLE hThread = CreateThread(NULL, 0, RegisterInterfaceThread, &state, 0, NULL);
        if (hThread == NULL) {
            DWORD err = GetLastError();
            fprintf(stderr, "Error CreateThread: 0x%08x\n", err);
            hr = err;
        }
        else {
            DWORD waitResult;
            if (WAIT_OBJECT_0 != (waitResult = WaitForSingleObject(state.hInterfaceRegistered, INFINITE))) {
                DWORD err = GetLastError();
                fprintf(stderr, "Error WaitForSingleObject: returned 0x%x; error=0x%08x\n", waitResult, err);
                hr = err;
            }
            else {
                fprintf(stdout, "Successfully waited for hInterfaceRegistered; dwCookie=0x%x\n", state.dwCookie);
                IGlobalInterfaceTable *pIGlobalInterfaceTable;
                if (FAILED(hr = CoCreateInstance
                    (
                    CLSID_StdGlobalInterfaceTable,
                    NULL,
                    CLSCTX_INPROC_SERVER,
                    IID_IGlobalInterfaceTable,
                    (void **)&pIGlobalInterfaceTable
                    ))) {
                    fprintf(stderr, "CoCreateInstance(StdGlobalInterfaceTable) failed with hresult 0x%x\n", hr);
                }
                else {
                    IUnknown *pv = NULL;
                    DWORD start_time = GetTickCount();
                    DWORD i;
                    for (i = 0; i != 10000; i++) {
                        if (FAILED(hr = pIGlobalInterfaceTable->GetInterfaceFromGlobal(state.dwCookie, IID_IRemoteProxyFactory, (LPVOID*)&pv))) {
                            fprintf(stderr, "GetInterfaceFromGlobal failed with hresult 0x%x\n", hr);
                            break;
                        }
                        else {
                            pv->Release();
                        }
                    }
                    DWORD end_time = GetTickCount();
                    DWORD difference = end_time - start_time;
                    fprintf(stdout, "%u iterations completed in %ums\n", i, difference);
                    pIGlobalInterfaceTable->Release();
                }
                if (!SetEvent(state.hShouldClose)) {
                    DWORD err = GetLastError();
                    fprintf(stderr, "SetEvent(hShouldClose) failed; err=0x%x\n", err);
                    hr = err;
                }
                else {
                    if (WAIT_OBJECT_0 != (waitResult = WaitForSingleObject(hThread, INFINITE))) {
                        DWORD err = GetLastError();
                        fprintf(stderr, "Error WaitForSingleObject(hThread): returned 0x%x; error=0x%08x\n", waitResult, err);
                        hr = err;
                    }
                    else {
                        printf("successfully joined thread.\n");
                    }
                }
            }
        }
    }
    return hr;
}

在 windbg 中运行它确认它会重新加载类型库 10000 次。

bp KERNELBASE!CreateFileW "r $t0 = @$t0 + 1; g"
bp OLEAUT32!LoadTypeLibEx "r $t1 = @$t1 + 1; g"
g
r $t0, $t1
$t0=000000000000c35c $t1=0000000000002712
于 2015-04-08T09:20:14.123 回答