【问题标题】:Using different API for different Windows versions efficiently有效地为不同的 Windows 版本使用不同的 API
【发布时间】:2014-11-11 14:20:07
【问题描述】:

我需要在可用的情况下使用新的 Windows 运行时功能,并在不存在新功能的情况下回退到旧的。

代码路径对性能至关重要。如果平台(Windows 8 / Windows Server 2012)提供,我需要调用GetSystemTimePreciseAsFileTime,如果新版本不存在,则回退到GetSystemTimeAsFileTime

我知道我可以使用LoadLibraryGetProcAddress 来加载GetSystemTimePreciseAsFileTime,如果失败,请退回到GetSystemTimeAsFileTime。我担心通过另一层间接调用像这样对性能至关重要的东西对性能的影响。

是否有任何内置机制可以延迟加载符号,而不是整个 DLL?所以我可以在启动时测试 Windows 版本,并在 Win8 上调用使用 GetSystemTimePreciseAsFileTime 的函数,否则调用使用 GetSystemTimeAsFileTime 的函数,而不会导致整个可执行文件在旧 Windows 版本上因链接错误而失败?

或者我应该停止担心并使用GetProcAddress

【问题讨论】:

  • 我会说“别担心,使用 GetProcAddress”。额外的间接性可能非常小。
  • 尝试进入 GetSystemTimeAsFileTime,一次使用 GetProcAddr,一次不使用,可能几乎没有区别。
  • 没有“间接层”。否则,分析器或小型测试程序可以轻松向您展示。
  • 您最多需要两次调用 GetProcAddress。为什么要担心在启动时执行了两次且不再运行的函数调用的性能?

标签: c windows winapi dll


【解决方案1】:

我认为andlabs的回答可能是对的;通过指针调用函数的性能损失不太可能很大。另一方面,它确实需要额外的 RAM 往返行程(否则会导致指针占用缓存中可能更好使用的行),因此它不是绝对确定的。

如果分析表明您确实有问题,请考虑将性能关键代码放入 DLL。有两个版本的 DLL,每个 API 一个。安装您的应用程序后,放置适当的版本。

【讨论】:

    【解决方案2】:

    正如对该问题的各种 cmet 所说,GetProcAddress() 的成本是最低的。通过函数指针调用函数的成本也接近于零。

    但是假设我们的工作非常严格。我们观察到您要调用的两个函数具有签名

    VOID WINAPI GetSystemTimePreciseAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
    void WINAPI GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
    

    也就是说,它们具有几乎相同的签名;唯一的区别是VOID...which MSDN says is just a macro that expands to void 的奇怪返回类型,所以它们完全一样。

    这是什么意思?这意味着您可以使用单个函数指针变量,在程序开始时初始化一次,如下所示:

    // global scope
    VOID (*WINAPI myGetSystemTime)(LPFILETIME);
    
    // in your initalization code
    FARPROC fp;
    myGetSystemTime = GetSystemTimeAsFileTime;
    if (/* LoadLibraryW(L"kernel32.dll") worked */) {
        fp = GetProcAddress(/* library handle */, "GetSystemTimePreciseAsFileTime");
        if (fp != NULL)
            myGetSystemTime = (VOID (*WINAPI)(LPFILETIME)) fp;
    }
    // and don't worry about errors from LoadLibraryW() or GetProcAddress() since we want to use the fallback; you may log a warning if you so choose
    

    然后您需要做的就是在需要有选择地调用函数时调用(*myGetSystemTime)(&time)。没有条件分支或不断轮询 DLL,并且应该没有可测量的性能影响。

    【讨论】:

    • 如果签名不同,您可以在它们周围放置一个包装函数,并存储指向 that 的指针。
    猜你喜欢
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-30
    • 2015-11-01
    • 2010-09-09
    相关资源
    最近更新 更多