这是一个由两部分组成的问题,我想将我的代码发布到堆栈上以帮助其他人完成相同的任务。
问题一:
我有一个代码子集,我相信它可以根据测量间隔正确测量 CPU 使用情况(根据检索的时间跨系统中的尽可能多的核心) - 我在线程调用中使用 1 秒。
我必须从网上极少数的文章和 C++ 代码中破译这一点。我的问题是,对于问题1,我所做的正确吗?
有时返回的值是负数,这就是我乘以-1的原因。再次,由于文档很少,我假设这就是我应该做的。
我有以下代码:
public static class Processor
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetSystemTimes(out ComTypes.FILETIME lpIdleTime, out ComTypes.FILETIME lpKernelTime, out ComTypes.FILETIME lpUserTime);
private static TimeSpan _sysIdleOldTs;
private static TimeSpan _sysKernelOldTs;
private static TimeSpan _sysUserOldTs;
static Processor()
{
}
public static void Test()
{
ComTypes.FILETIME sysIdle, sysKernel, sysUser;
if(GetSystemTimes(out sysIdle, out sysKernel, out sysUser))
{
TimeSpan sysIdleTs = GetTimeSpanFromFileTime(sysIdle);
TimeSpan sysKernelTs = GetTimeSpanFromFileTime(sysKernel);
TimeSpan sysUserTs = GetTimeSpanFromFileTime(sysUser);
TimeSpan sysIdleDiffenceTs = sysIdleTs.Subtract(_sysIdleOldTs);
TimeSpan sysKernelDiffenceTs = sysKernelTs.Subtract(_sysKernelOldTs);
TimeSpan sysUserDiffenceTs = sysUserTs.Subtract(_sysUserOldTs);
_sysIdleOldTs = sysIdleTs;
_sysKernelOldTs = sysKernelTs;
_sysUserOldTs = sysUserTs;
TimeSpan system = sysKernelDiffenceTs.Add(sysUserDiffenceTs);
Double cpuUsage = (((system.Subtract(sysIdleDiffenceTs).TotalMilliseconds) * 100) / system.TotalMilliseconds);
if (cpuUsage < 0)
{
Console.WriteLine("CPU: " + ((int) (cpuUsage)*-1) + "%");
}
else
{
Console.WriteLine("CPU: " + (int) (cpuUsage) + "%");
}
Console.WriteLine("");
}
else
{
Console.WriteLine("Couldn't get CPU usage!");
Console.WriteLine("");
}
}
private static TimeSpan GetTimeSpanFromFileTime(ComTypes.FILETIME time)
{
return TimeSpan.FromMilliseconds((((ulong)time.dwHighDateTime << 32) + (uint)time.dwLowDateTime) * 0.000001);
}
}
问题2:
无论如何,我是否可以将程序中的线程与 Windows 任务管理器的线程同步,以便将测量数据(例如 CPU 使用率)与上述代码相匹配?
我的意思是,如果您打开 Windows 任务管理器,您会注意到它每秒轮询一次 - 实际上它不需要比这个少。我想做的就是将时间与我的线程相匹配。
因此,当 Windows 任务管理器轮询时,我的线程也会轮询。
一些注意事项:
我不想使用性能计数器或 .NET 内置方法。事实上,我相信 - 根据我所读到的内容,.NET 没有计算计算机上 CPU 使用率的方法,否则需要性能计数器。
性能计数器有开销,此外还会导致 GC 增长,更不用说调用下一个结果的延迟。虽然我的软件不需要实时性能,但我确实需要它具有尽可能高的响应速度并使用尽可能少的 CPU 时间。上面的代码可以在不到一毫秒的时间内调用并返回。事实上,在我的开发机器上,时间跨度差异显示为 0ms。 我不认为性能计数器具有如此灵敏的响应能力。
如果您好奇的话,我的软件正在收集许多项目,CPU、内存、事件日志项目等,其中所有这些都需要在下一次轮询之前(1 秒后)收集并存储在 SQL CE 中。然而,每个任务、项目都在其自己的线程上以促进这一点。
另外,上面的代码无论如何都没有优化,你会注意到我还没有评论它。原因是我想在优化等之前确保它是正确的。
Update 1
根据我一路上发表的评论,我删除了额外的“系统”时间跨度,因为它不是必需的,并修改了检索“CPU 使用率”的行并对其进行了适当的转换。
int cpuUsage = (int)(((sysKernelDifferenceTs.Add(sysUserDifferenceTs).Subtract(sysIdleDifferenceTs).TotalMilliseconds) * 100.00) / sysKernelDifferenceTs.Add(sysUserDifferenceTs).TotalMilliseconds);
虽然我仍然不确定这个公式。虽然它似乎非常准确,但它有时会返回负数,这就是为什么我将其乘以 -1(如果是这种情况)。毕竟,不存在 -2% CPU 使用率等情况。
Update 2
所以我使用“System.Diagnostics.PerformanceCounter”做了一个简单的测试。虽然非常方便并且完全按照预期执行,但它确实会产生开销。
以下是我的观察:
- 性能计数器的初始化花费了更长的时间。在我的 i7 2.6 Ghz 上大约长了三秒。
- 性能计数器似乎还仅仅通过使用它就增加了大约 5MB 的 RAM 使用量。我的意思是:使用上面的代码,我的应用程序最大内存为 7.5MB。使用性能计数器时,它“起始”为 12.5MB。
- 在 5 秒的时间内,我的线程运行了 5 次 - 每秒一次,我的应用程序的内存增长了 1 MB,这种增长与时间一致,尽管在我的情况下它确实稳定了 3-4MB以上开始。因此,如果我的应用程序的内存通常为 7.5MB,上面的代码,则 PC 代码稳定在 16.5MB 内存 - 比上面的代码增加了 9MB。Note:上面的代码does not导致这种增加。
因此,如果您的应用程序是以资源使用和计时为关键的方式构建的,那么由于这些原因,我建议不要使用性能计数器。否则就继续吧,因为它可以正常工作,不会出现任何混乱。
至于我的应用程序,性能计数器将不利于我的软件的目的。