上下文切换通常发生在调用函数和执行函数之间吗?
Does context switching usually happen between calling a function, and executing it?
所以我一直在研究一个复杂应用程序(由数百名程序员编写)的源代码。除此之外,我还创建了一些时间检查函数,以及合适的数据结构来测量主循环不同部分的执行周期,运行 对这些测量结果进行了一些分析。
这是一个有助于解释的伪代码:
main()
{
TimeSlicingSystem::AddTimeSlice(0);
FunctionA();
TimeSlicingSystem::AddTimeSlice(3);
FuncitonB();
TimeSlicingSystem::AddTimeSlice(6);
PrintTimeSlicingValues();
}
void FunctionA()
{
TimeSlicingSystem::AddTimeSlice(1);
//...
TimeSlicingSystem::AddTimeSlice(2);
}
FuncitonB()
{
TimeSlicingSystem::AddTimeSlice(4);
//...
TimeSlicingSystem::AddTimeSlice(5);
}
PrintTimeSlicingValues()
{
//Prints the different between each slice, and the slice before it,
//starting from slice number 1.
}
大多数测量都非常合理,例如,为局部变量赋值的成本不到一微秒。大多数功能会在几微秒内从头到尾执行,很少会达到一毫秒。
然后我 运行 进行了半小时左右的一些测试,我发现了一些我不太理解的 st运行ge 结果。某些函数将被调用,并且在测量从调用函数的那一刻('calling' 代码中的最后一行)到 'called' 函数中的第一行将花费很长时间,最多一个 30 毫秒的周期。这是在一个循环中发生的,否则该循环将在不到 8 毫秒的时间内完成一个完整的迭代。
为了得到它的图片,在我包含的伪代码中,测量了切片编号 0 和切片编号 1 之间的时间段,或者切片编号 3 和切片编号 4 之间的时间。这就是我所指的那种时期。它是调用一个函数和 运行在被调用函数中的第一行之间的测量时间。
问题A。此行为可能是由于 OS 的线程或进程切换造成的吗?调用一个函数是一个独特的漏洞吗?我正在处理的 OS 是 Windows 10.
有趣的是,在 'calling' 代码问题中调用后函数中从来没有最后一行返回到第一行(从切片编号 2 到 3 或从 5 到 6 的句点伪代码)!并且所有测量值始终小于 5 微秒。
问题B。无论如何,这可能是由于我使用的时间测量方法造成的吗?由于时钟差异,不同内核之间的切换是否会暗示上下文切换比实际慢? (尽管到目前为止我还没有发现一个负增量时间,这似乎完全驳斥了这个假设)。同样,我正在处理的 OS 是 Windows 10.
我的时间测量函数看起来是这样的:
FORCEINLINE double Seconds()
{
Windows::LARGE_INTEGER Cycles;
Windows::QueryPerformanceCounter(&Cycles);
// add big number to make bugs apparent where return value is being passed to float
return Cycles.QuadPart * GetSecondsPerCycle() + 16777216.0;
}
QuestionA. Could this behavior be due to thread, or process switching by the OS?
是的。线程切换随时可能发生(例如,当设备发送 IRQ 导致另一个更高优先级的线程解除阻塞并立即抢占您的线程时),这 can/will 会导致您的线程出现意外的时间延迟。
Does calling a function is a uniquely vulnerable spot to that?
调用您自己的函数并没有什么特别之处使它们特别容易受到攻击。如果函数涉及内核的 API 线程切换的可能性更大,有些事情(例如调用“sleep()
”)几乎可以保证导致线程切换。
还有与虚拟内存管理的潜在交互 - 通常情况下(例如您的可执行文件、您的代码、您的数据)使用“内存映射文件”,第一次访问它可能会导致 OS 获取来自磁盘的代码或数据(并且您的线程可以被阻塞,直到它想要的代码或数据从磁盘到达);很少使用的代码或数据也可以发送到 swap space 并需要获取。
QuestionB. Could this be, in any way, due to the time measurement method I am using?
在实践中,Windows' QueryPerformanceCounter()
很可能是用 RDTSC
指令实现的(假设 80x86 CPU/s)并且根本不涉及内核,对于现代硬件,这很可能是单原子的。理论上 Windows 可以模仿 RDTSC
and/or 以另一种方式实现 QueryPerformanceCounter()
以防止安全问题(时序边信道),正如英特尔已经推荐了大约 30 年,但这不太可能(现代操作系统,包括但不限于 Windows,往往更关心性能而不是安全);理论上你的 hardware/CPU 可能太老了(大约 10 岁以上)以至于 Windows 必须以不同的方式实现 QueryPerformanceCounter()
,或者你可以使用其他一些 CPU(例如 ARM 而不是 80x86)。
换句话说;您使用的时间测量方法不太可能(但并非不可能)导致任何计时问题。
所以我一直在研究一个复杂应用程序(由数百名程序员编写)的源代码。除此之外,我还创建了一些时间检查函数,以及合适的数据结构来测量主循环不同部分的执行周期,运行 对这些测量结果进行了一些分析。
这是一个有助于解释的伪代码:
main()
{
TimeSlicingSystem::AddTimeSlice(0);
FunctionA();
TimeSlicingSystem::AddTimeSlice(3);
FuncitonB();
TimeSlicingSystem::AddTimeSlice(6);
PrintTimeSlicingValues();
}
void FunctionA()
{
TimeSlicingSystem::AddTimeSlice(1);
//...
TimeSlicingSystem::AddTimeSlice(2);
}
FuncitonB()
{
TimeSlicingSystem::AddTimeSlice(4);
//...
TimeSlicingSystem::AddTimeSlice(5);
}
PrintTimeSlicingValues()
{
//Prints the different between each slice, and the slice before it,
//starting from slice number 1.
}
大多数测量都非常合理,例如,为局部变量赋值的成本不到一微秒。大多数功能会在几微秒内从头到尾执行,很少会达到一毫秒。
然后我 运行 进行了半小时左右的一些测试,我发现了一些我不太理解的 st运行ge 结果。某些函数将被调用,并且在测量从调用函数的那一刻('calling' 代码中的最后一行)到 'called' 函数中的第一行将花费很长时间,最多一个 30 毫秒的周期。这是在一个循环中发生的,否则该循环将在不到 8 毫秒的时间内完成一个完整的迭代。
为了得到它的图片,在我包含的伪代码中,测量了切片编号 0 和切片编号 1 之间的时间段,或者切片编号 3 和切片编号 4 之间的时间。这就是我所指的那种时期。它是调用一个函数和 运行在被调用函数中的第一行之间的测量时间。
问题A。此行为可能是由于 OS 的线程或进程切换造成的吗?调用一个函数是一个独特的漏洞吗?我正在处理的 OS 是 Windows 10.
有趣的是,在 'calling' 代码问题中调用后函数中从来没有最后一行返回到第一行(从切片编号 2 到 3 或从 5 到 6 的句点伪代码)!并且所有测量值始终小于 5 微秒。
问题B。无论如何,这可能是由于我使用的时间测量方法造成的吗?由于时钟差异,不同内核之间的切换是否会暗示上下文切换比实际慢? (尽管到目前为止我还没有发现一个负增量时间,这似乎完全驳斥了这个假设)。同样,我正在处理的 OS 是 Windows 10.
我的时间测量函数看起来是这样的:
FORCEINLINE double Seconds()
{
Windows::LARGE_INTEGER Cycles;
Windows::QueryPerformanceCounter(&Cycles);
// add big number to make bugs apparent where return value is being passed to float
return Cycles.QuadPart * GetSecondsPerCycle() + 16777216.0;
}
QuestionA. Could this behavior be due to thread, or process switching by the OS?
是的。线程切换随时可能发生(例如,当设备发送 IRQ 导致另一个更高优先级的线程解除阻塞并立即抢占您的线程时),这 can/will 会导致您的线程出现意外的时间延迟。
Does calling a function is a uniquely vulnerable spot to that?
调用您自己的函数并没有什么特别之处使它们特别容易受到攻击。如果函数涉及内核的 API 线程切换的可能性更大,有些事情(例如调用“sleep()
”)几乎可以保证导致线程切换。
还有与虚拟内存管理的潜在交互 - 通常情况下(例如您的可执行文件、您的代码、您的数据)使用“内存映射文件”,第一次访问它可能会导致 OS 获取来自磁盘的代码或数据(并且您的线程可以被阻塞,直到它想要的代码或数据从磁盘到达);很少使用的代码或数据也可以发送到 swap space 并需要获取。
QuestionB. Could this be, in any way, due to the time measurement method I am using?
在实践中,Windows' QueryPerformanceCounter()
很可能是用 RDTSC
指令实现的(假设 80x86 CPU/s)并且根本不涉及内核,对于现代硬件,这很可能是单原子的。理论上 Windows 可以模仿 RDTSC
and/or 以另一种方式实现 QueryPerformanceCounter()
以防止安全问题(时序边信道),正如英特尔已经推荐了大约 30 年,但这不太可能(现代操作系统,包括但不限于 Windows,往往更关心性能而不是安全);理论上你的 hardware/CPU 可能太老了(大约 10 岁以上)以至于 Windows 必须以不同的方式实现 QueryPerformanceCounter()
,或者你可以使用其他一些 CPU(例如 ARM 而不是 80x86)。
换句话说;您使用的时间测量方法不太可能(但并非不可能)导致任何计时问题。