与 i7 处理器相比,使用 Windows API CreateWaitableTimer 休眠在 Xeon 上的执行方式不同

Sleeping using Windows API CreateWaitableTimer executes differently on Xeon compared to i7 processor

我正在开发一个需要持续约 1 毫秒睡眠的程序。睡眠用于生成长度为 ~1 ms 的硬件脉冲。

我正在使用以下代码进行睡眠

void usleep(__int64 usec)
{
  HANDLE timer;
  LARGE_INTEGER ft;
  ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time

  timer = CreateWaitableTimer(NULL, TRUE, NULL);
  SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
  WaitForSingleObject(timer, INFINITE);
  CloseHandle(timer);
}

取自here

当我在 Intel i7 上使用上述代码(使用 Embarcaderos bcc32 编译器)时,通过 1000(1 毫秒)我得到一个睡眠,我使用 Poco's timestamp 函数测量到大约 1 毫秒。代码本身在线程中执行。

代码如下所示:

    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 0 << "\n";
    setHigh(false);
    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 1 << "\n";       
    usleep(1000);
    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 1 << "\n";
    setLow(false);
    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 0 << "\n";

其中 mDebugFile 是一个 fstream 对象,setLow/setHigh 是对硬件的调用。

但是,当在 a Xeon CPU 上执行相同的代码时,测得的睡眠约为 10 毫秒 . 假设Poco的计时函数给出了合适的时间,要求1ms的时候10ms已经很大了。

有没有其他方法可以可靠地睡眠约 1 毫秒?可以修改 Windows OS 以提供更可靠的睡眠吗?

我无法访问 boost 或更高版本的 C++11 功能。

Windows 不是实时操作系统,因此无法保证在用户代码中获得超精确的睡眠时间。有一堆东西在玩。

  • OS 可能会改变过期时间以节省电量。这叫做timer coalescing。唤醒一次来处理一些事件比以更高的频率更频繁地唤醒更节能。如果 OS 对不同类型的硬件使用不同的策略也就不足为奇了。

  • WaitForSingleObject(和相关 API)仍然受制于调度程序。当对象变为有信号时,等待线程变得可用 可用于调度,但这并不意味着它会立即被调度。这取决于进程和线程的优先级、可用内核、系统的量子间隔、月相等。

有两个 API 可以增加 Windows 上的默认计时器分辨率:NtSetTimerResolution(官方未记录)或 timeBeginPeriod。如果机器没有过载,您可以使用基本方法(例如 Sleep)在用户代码中获得非常接近 1 毫秒的间隔。

但是很多节目waste energy abusing these APIs。 (如果您在机器上看到 1 毫秒的分辨率,可能是因为某些程序已经提高了计时器分辨率。)长期以来,这一直被认为是不好的做法。计时器分辨率是系统范围的设置,因此它会影响计算机上的所有内容 运行。如果您必须提高分辨率,请仅在需要时进行,并确保在完成后立即恢复默认值。

如果你需要更精确,我相信你需要制作一个内核模式驱动程序,但我没有这方面的经验。在某些时候,Windows 添加了一个音频堆栈来保证 20 us 或更少的延迟。如果我没记错的话,那需要内核模式恶作剧。

您可以考虑使用微控制器(例如 Arduino)来生成硬件信号并让 Windows 程序通过串行接口向其发送命令。