我正在开发一个应用程序,用户可以看到某些东西,并且必须通过单击键盘上的键来做出反应。反应时间至关重要,越精确越好。
我只用几行代码编写了示例app inf WPF来测试默认设置:
namespace Test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Stopwatch sw;
public MainWindow()
{
InitializeComponent();
sw = new Stopwatch();
sw.Start();
this.KeyDown += OnKeyDown;
}
private void OnKeyDown(object sender, KeyEventArgs keyEventArgs)
{
sw.Stop();
lbl.Content = sw.ElapsedMilliseconds;
sw.Restart();
}
}
}
lbl
是一个简单的标签。
奇怪的是,当我按下例如空格并按住它时,lbl
的值在30-33范围内变化。
所以我无法预测响应准确度是多少?例如1毫秒精度是不可能的?用户同时点击空间(例如1毫秒精度)我可以在事件处理程序中处理它吗?
主要问题是:
假设我有一个按键事件处理程序:
Test_KeyDown(object sender, KeyEventArgs keyEventArgs)
{
time = stopwatch.elapsed();
stopwatch.Restart();
}
什么是可能发生的“时间”的最小值?我能确定时间值精确到1毫秒吗?在这种方法中,我启动秒表但是我必须等待 - 多长时间 - 刷新GUI?
你的测试肯定是无效的,它只测量其他几个贡献者所指出的键盘重复率。但是它有意无益,你实际上可以看到键盘没有问题。
Windows中的大多数事件的发生速率取决于时钟中断速率。默认情况下,每秒刻录64次,每15.625毫秒一次。这唤醒了内核并且它正在寻找是否需要完成某些事情,寻找传递给处理器核心的工作。最典型的是无所事事,核心是通过HLT指令关闭的。直到下一个中断发生。
所以一个问题是你的测试永远不会比15.625毫秒更准确。而且你的观察结果恰巧与你所看到的相匹配的数字是两倍。但事实并非如此,您可以使用您的程序来查看。使用控制面板+键盘并调整“重复速率”滑块。请注意如何调整它并将数字更改为不是15.625的倍数的值。
这不完全是一个意外,键盘控制器也会产生一个中断,就像时钟一样。你有充分的证据证明这个中断本身已经足够好,可以让你的程序重新激活。你可以看出键盘控制器本身足够快,扫描键盘矩阵。键盘上的错误栏不会大于+/- 2毫秒,大约是您在显示的数字中看到的噪音。如果你的键盘扫描速度较慢,你可以通过这个测试消除它。
你有更大的担忧是视频。视频适配器通常以每秒60次更新刷新LCD监视器。在最坏的情况下,测试对象将无法在17毫秒内物理地看到图像。液晶显示器本身也不是那么快,便宜的显示器的响应时间为16毫秒或更差。液体中的水晶的副作用不能足够快地翻转。
消除刷新率错误需要您编程与vertical blanking interval同步。你可以用DirectX做些什么。您可以找到响应时间约为4毫秒的高档液晶显示器,深受游戏玩家欢迎。
首先,如果Stopwatch.IsHighResolution
是true
,那么Stopwatch
使用QueryPerformanceCounter
,它可以测量<1 ms分辨率的时间间隔。
其次,当您按住空格键时,Windows会重复开始发送WM_KEYDOWN消息,秒表将测量这些消息之间的间隔。此时间间隔由注册表项HKCU\Control Panel\Keyboard\KeyboardSpeed
确定。
它的默认值是31,这是最快的重复率,这意味着每秒大约30个字符。这就是您测量大约1000/30 = 33 ms间隔的原因。
如果你把它设置为0,也就是说,设置为最慢的重复率,这意味着每秒大约2个字符,那么你应该测量大约。间隔500毫秒。我用这个设置测试了你的代码,我确实得到了500毫秒。 (更改KeyboardSpeed后别忘了重启Windows!)
您需要捕获单个keydown事件,而不是重复事件,因此您不必更改KeyboardSpeed设置。您的程序应该只向用户显示对象,启动秒表,并在发生keydown事件时停止它。 ElapsedMilliseconds
将给出反应时间。多次测量并使用平均值。
问题是,即使QueryPerformanceCounter
准确测量经过的时间,键盘和Windows本身也会引起延迟,这会增加测量的反应时间。此外,延迟不是恒定的:如果Windows在应该处理keydown事件的那一刻忙,那么延迟将会更大。因此,如果你认真对待这个任务,你应该校准你的程序。
我的意思是,您应该购买或构建一个基于微控制器的小型电子设备,该设备打开LED并检测打开LED和用户按下按钮之间所经过的时间。使用此设备进行10-20(越多越好)反应时间测量,并与同一测试人员一起,使用您的程序再进行10-20次测量。两者之间的差异将给你键盘和Windows造成的延迟。可以从程序测量的反应时间中减去这种差异。
(人们可能会问,为什么你不应该使用小而精确的电子设备而不是Windows应用程序。首先,制造和销售软件比制造和销售硬件更容易和更便宜。其次,视觉对象可能很复杂(例如,棋盘格),可以在PC上更有效地渲染复杂的对象。)
我想在此指出另一种追踪时机的工具。由于您正在考虑测试应用程序的响应,并且正如有人提到这涉及操作系统的消息,您可以利用Spy ++来查看这些消息的时间。我将按空格的输出复制到我一直在监听键盘消息的窗口,打开了所有输出。我按下了一次空间,然后尽可能快地在通过扩展坞的USB键盘上释放。你可以看到它需要〜。05ms来处理。
<00001> 00090902 P WM_KEYDOWN nVirtKey:VK_SPACE cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000020 lParam:00390001 time:1:07:38.116 point:(183, 290)]
<00002> 00090902 P WM_CHAR chCharCode:'32' (32) cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000020 lParam:00390001 time:1:07:38.116 point:(183, 290)]
<00003> 00090902 P WM_KEYUP nVirtKey:VK_SPACE cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:1 fUp:1 [wParam:00000020 lParam:C0390001 time:1:07:38.163 point:(183, 290)]
Spy ++是随Visual Studio提供的工具。您可以在C:\Program Files\Microsoft Visual Studio XYZ\Common7\Tools\spyxx.exe
找到它,XYZ是8,9.0和10.0,我可以确认。
你可以做些什么来进一步测试时间是让Spy ++监听键盘命令和WM_PAINT等等,看看程序响应键盘消息的UI变化有多快。
例如,下面是已经有3+3
的计算器,然后按Enter
后的干净日志。您可以看到计算器能够在KeyDown和KeyUp之间的.062ms之前进行计算和显示。
<00001> 00090902 P WM_KEYDOWN nVirtKey:VK_RETURN cRepeat:1 ScanCode:1C fExtended:1 fAltDown:0 fRepeat:0 fUp:0 [wParam:0000000D lParam:011C0001 time:1:19:12.539 point:(179, 283)]
<00002> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000]
<00003> 00090902 R WM_PAINT lResult:00000000
<00004> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000]
<00005> 00090902 R WM_PAINT lResult:00000000
<00006> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000]
<00007> 00090902 R WM_PAINT lResult:00000000
<00008> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000]
<00009> 00090902 R WM_PAINT lResult:00000000
<00010> 00090902 P WM_KEYUP nVirtKey:VK_RETURN cRepeat:1 ScanCode:1C fExtended:1 fAltDown:0 fRepeat:1 fUp:1 [wParam:0000000D lParam:C11C0001 time:1:19:12.601 point:(179, 283)]
编辑 - 在Spy ++中,我建议使用Logging Options,它会显示Message Options对话框。转到消息选项卡,单击全部清除,选中'键盘',然后滚动列表框并选择WM_PAINT。这样,您只有所需的消息,否则您将被它们淹没。