代码执行"latencies"从何而来?
Where do code execution "latencies" come from?
我遇到的问题是代码执行经常出现延迟,我无法解释。对于延迟,我的意思是执行一段应该需要恒定时间的代码,有时需要更多时间。
我附加了一个小的 C 程序,它在 CPU 核心 1 上执行一些“虚拟”计算。线程固定到这个核心。我已经在具有 192 GiB RAM 和 96 CPU 内核的 Ubuntu 18.04 机器上执行了它。这台机器什么都不做。
该工具只运行一个线程(主线程正在休眠)并且至少perf
工具没有显示开关(线程开关),所以这应该不是问题。
该工具的输出如下所示(或多或少每秒显示一次):
...
Stats:
Max [us]: 883
Min [us]: 0
Avg [us]: 0.022393
...
这些统计信息始终显示 1'000'000 次运行的结果。我的问题是为什么最大值总是那么大?此外,99.99% 的分位数通常很大(我没有将它们添加到示例中以使代码变小;最大值也很好地显示了这种行为)。为什么会发生这种情况,我该如何避免?在某些应用程序中,这种“差异”对我来说是个大问题。
鉴于没有别的东西运行,我很难理解这些值。
非常感谢
main.c:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <sys/time.h>
#include <pthread.h>
#include <sys/sysinfo.h>
static inline unsigned long now_us()
{
struct timeval tx;
gettimeofday(&tx, NULL);
return tx.tv_sec * 1000000 + tx.tv_usec;
}
static inline int calculate(int x)
{
/* Do something "expensive" */
for (int i = 0; i < 1000; ++i) {
x = (~x * x + (1 - x)) ^ (13 * x);
x += 2;
}
return x;
}
static void *worker(void *arg)
{
(void)arg;
const int runs_per_measurement = 1000000;
int dummy = 0;
while (true) {
int max_us = -1;
int min_us = -1;
int sum_us = 0;
for (int i = 0; i < runs_per_measurement; ++i) {
const long start_us = now_us();
dummy = calculate(dummy);
const long runtime_us = now_us() - start_us;
/* Update stats */
if (max_us < runtime_us) {
max_us = runtime_us;
}
if (min_us < 0 || min_us > runtime_us) {
min_us = runtime_us;
}
sum_us += runtime_us;
}
printf("Stats:\n");
printf(" Max [us]: %d\n", max_us);
printf(" Min [us]: %d\n", min_us);
printf(" Avg [us]: %f\n", (double)sum_us / runs_per_measurement);
printf("\n");
}
return NULL;
}
int main()
{
pthread_t worker_thread;
if (pthread_create(&worker_thread, NULL, worker, NULL) != 0) {
printf("Cannot create thread!\n");
return 1;
}
/* Use CPU number 1 */
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
if (pthread_setaffinity_np(worker_thread, sizeof(cpuset), &cpuset) != 0) {
printf("Cannot set cpu core!\n");
return 1;
}
pthread_join(worker_thread, NULL);
return 0;
}
生成文件:
main: main.c
gcc -o $@ $^ -Ofast -lpthread -Wall -Wextra -Werror
这是多处理如何在操作系统中工作的一个很好的例子。
如上评论所述:
"This machine does nothing else" --> absurd. Run ps -e to get an idea of all the other things your machine is doing. – John Bollinger
这是通过操作系统(特别是内核)让一个任务 运行 执行一段时间,然后暂停它并允许另一个任务 运行.
来实现的
有效地,您的代码先 运行 一段时间,然后其他 运行 暂停,然后 运行 一段时间,依此类推。
这说明了您看到的时间变化,因为您测量的是经过的时间,而不是 'cpu-time'(实际花费的时间 运行ning)。 C 有一些用于测量 cpu 时间的标准函数,例如来自 GNU
的 this
CPU 更详细地介绍了调度 here
最后,为了不成为Pre-empted,您需要运行您的代码:Kenel-space、[=41] =],或在 'real-time' 操作系统中。
(我会让你 google 这些术语是什么意思 :-) )
唯一的其他解决方案是探索 linux/unix 'nice values'(我也会让你 google 这个,但基本上它会为你的进程分配更高或更低的优先级.)
如果您对这类事情感兴趣,可以阅读 Robert Love 的一本名为 'Linux Kernel Development'
的好书
我遇到的问题是代码执行经常出现延迟,我无法解释。对于延迟,我的意思是执行一段应该需要恒定时间的代码,有时需要更多时间。
我附加了一个小的 C 程序,它在 CPU 核心 1 上执行一些“虚拟”计算。线程固定到这个核心。我已经在具有 192 GiB RAM 和 96 CPU 内核的 Ubuntu 18.04 机器上执行了它。这台机器什么都不做。
该工具只运行一个线程(主线程正在休眠)并且至少perf
工具没有显示开关(线程开关),所以这应该不是问题。
该工具的输出如下所示(或多或少每秒显示一次):
...
Stats:
Max [us]: 883
Min [us]: 0
Avg [us]: 0.022393
...
这些统计信息始终显示 1'000'000 次运行的结果。我的问题是为什么最大值总是那么大?此外,99.99% 的分位数通常很大(我没有将它们添加到示例中以使代码变小;最大值也很好地显示了这种行为)。为什么会发生这种情况,我该如何避免?在某些应用程序中,这种“差异”对我来说是个大问题。
鉴于没有别的东西运行,我很难理解这些值。
非常感谢
main.c:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <sys/time.h>
#include <pthread.h>
#include <sys/sysinfo.h>
static inline unsigned long now_us()
{
struct timeval tx;
gettimeofday(&tx, NULL);
return tx.tv_sec * 1000000 + tx.tv_usec;
}
static inline int calculate(int x)
{
/* Do something "expensive" */
for (int i = 0; i < 1000; ++i) {
x = (~x * x + (1 - x)) ^ (13 * x);
x += 2;
}
return x;
}
static void *worker(void *arg)
{
(void)arg;
const int runs_per_measurement = 1000000;
int dummy = 0;
while (true) {
int max_us = -1;
int min_us = -1;
int sum_us = 0;
for (int i = 0; i < runs_per_measurement; ++i) {
const long start_us = now_us();
dummy = calculate(dummy);
const long runtime_us = now_us() - start_us;
/* Update stats */
if (max_us < runtime_us) {
max_us = runtime_us;
}
if (min_us < 0 || min_us > runtime_us) {
min_us = runtime_us;
}
sum_us += runtime_us;
}
printf("Stats:\n");
printf(" Max [us]: %d\n", max_us);
printf(" Min [us]: %d\n", min_us);
printf(" Avg [us]: %f\n", (double)sum_us / runs_per_measurement);
printf("\n");
}
return NULL;
}
int main()
{
pthread_t worker_thread;
if (pthread_create(&worker_thread, NULL, worker, NULL) != 0) {
printf("Cannot create thread!\n");
return 1;
}
/* Use CPU number 1 */
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
if (pthread_setaffinity_np(worker_thread, sizeof(cpuset), &cpuset) != 0) {
printf("Cannot set cpu core!\n");
return 1;
}
pthread_join(worker_thread, NULL);
return 0;
}
生成文件:
main: main.c
gcc -o $@ $^ -Ofast -lpthread -Wall -Wextra -Werror
这是多处理如何在操作系统中工作的一个很好的例子。
如上评论所述:
"This machine does nothing else" --> absurd. Run ps -e to get an idea of all the other things your machine is doing. – John Bollinger
这是通过操作系统(特别是内核)让一个任务 运行 执行一段时间,然后暂停它并允许另一个任务 运行.
来实现的有效地,您的代码先 运行 一段时间,然后其他 运行 暂停,然后 运行 一段时间,依此类推。
这说明了您看到的时间变化,因为您测量的是经过的时间,而不是 'cpu-time'(实际花费的时间 运行ning)。 C 有一些用于测量 cpu 时间的标准函数,例如来自 GNU
的 thisCPU 更详细地介绍了调度 here
最后,为了不成为Pre-empted,您需要运行您的代码:Kenel-space、[=41] =],或在 'real-time' 操作系统中。 (我会让你 google 这些术语是什么意思 :-) )
唯一的其他解决方案是探索 linux/unix 'nice values'(我也会让你 google 这个,但基本上它会为你的进程分配更高或更低的优先级.)
如果您对这类事情感兴趣,可以阅读 Robert Love 的一本名为 'Linux Kernel Development'
的好书