Performance 是什么导致我的运行时间比用户时间长得多?

Performance 是什么导致我的运行时间比用户时间长得多?,performance,r,Performance,R,我对一些R语句进行了基准测试(请参阅详细信息),发现我的运行时间比我的用户时间长得多 user system elapsed 7.910 7.750 53.916 有人能帮助我了解哪些因素(R或硬件)决定用户时间和运行时间之间的差异,以及我如何改进它吗?如果有帮助的话:我正在MacBookAir 1.7Ghz i5上运行data.table数据操作,内存为4GB 更新:我的粗略理解是,用户时间就是我的CPU处理工作所需的时间。已用时间是指从提交作业到返回数据的时间长度。在

我对一些R语句进行了基准测试(请参阅详细信息),发现我的运行时间比我的用户时间长得多

   user  system elapsed 
  7.910   7.750  53.916 
有人能帮助我了解哪些因素(R或硬件)决定用户时间和运行时间之间的差异,以及我如何改进它吗?如果有帮助的话:我正在MacBookAir 1.7Ghz i5上运行data.table数据操作,内存为4GB


更新:我的粗略理解是,用户时间就是我的CPU处理工作所需的时间。已用时间是指从提交作业到返回数据的时间长度。在处理8秒钟后,我的计算机还需要做什么

更新:正如评论中所建议的,我对两个数据运行了几次。表:Y,有104列(抱歉,随着时间的推移,我添加了更多列),X是Y的子集,只有3个键。以下是更新。请注意,我连续运行了这两个过程,因此内存状态应该相似

 X<- Y[, list(Year, MemberID, Month)]

 system.time(
   {X[ , Month:= -Month]
   setkey(X,Year, MemberID, Month)
   X[,Month:=-Month]}
  )
   user  system elapsed 
  3.490   0.031   3.519 

 system.time(
 {Y[ , Month:= -Month]
  setkey(Y,Year, MemberID, Month)
  Y[,Month:=-Month]}
 )
   user  system elapsed 
  8.444   5.564  36.284 

谢谢

用户时间是计算机计算所用的秒数。系统时间是操作系统响应程序请求所花费的时间。运行时间是这两个时间的总和,加上程序和/或操作系统必须执行的“等待”操作。需要注意的是,这些数字是所花费时间的总和。您的程序可能会计算1秒,然后在操作系统上等待1秒,然后在磁盘上等待3秒,并在运行时多次重复此循环

基于您的程序占用的系统时间和用户时间一样多这一事实,这是一个非常IO密集的事情。大量读取磁盘或大量写入磁盘。RAM非常快,通常只有几百纳秒。所以,如果所有东西都适合RAM,那么经过的时间通常只比用户时间长一点点。但磁盘可能需要几毫秒来查找,甚至需要更长的时间来回复数据。这要慢一百万倍

我们已确定您的处理器在~8+~8=~16秒内“运行”。它在另外的~54-~16=~38秒里做了什么?正在等待硬盘向其发送所需的数据

更新1:

马修提出了一些很好的观点,我正在做一些我可能不应该做的假设。Adam,如果您想发布表中所有行的列表(我们只需要数据类型),我们可以更好地了解发生了什么

我刚刚编写了一个不做任何事情的程序来验证我的假设,即不花在用户空间和内核空间的时间很可能花在等待IO上

#include <stdio.h>
int main()
{
    int i;
    for(i = 0; i < 1000000000; i++)
    {
        int j, k, l, m;
        j = 10;
        k = i;
        l = j + k;
        m = j + k - i + l;
    }
    return 0;
}
正如您通过检查所看到的,程序没有真正的工作,因此它不会要求内核做任何事情,除非将其加载到RAM中并开始运行。因此,几乎所有的“实时”时间都作为“用户”时间使用

现在是一个内核繁重的不做任何事情的程序(通过较少的迭代来保持时间合理):

同样,通过检查很容易看出程序只要求内核给它随机字节/dev/uradom是一个非阻塞的熵源。这是什么意思?内核使用伪随机数生成器为我们的小测试程序快速生成“随机”值。这意味着内核必须进行一些计算,但它可以很快返回。所以这个程序主要是等待内核为它进行计算,我们可以从几乎所有的时间都花在sys上这一事实中看到这一点

现在我们要做一个小小的改变。我们将从阻塞的/dev/random中读取,而不是从非阻塞的/dev/urandom中读取。这是什么意思?它不做太多的计算,而是等待在您的计算机上发生的事情,内核开发人员根据经验确定这些事情是随机的。(我们还将进行更少的迭代,因为这需要更长的时间)

运行起来花了41秒,但在用户和real上的时间却少得可怜。为什么呢?所有的时间都花在内核上,但没有进行活动计算。内核只是在等待事情发生。一旦收集到足够的熵,内核就会唤醒并将数据发送回程序。(请注意,在您的计算机上运行可能需要更少或更多的时间,具体取决于所发生的一切)。我认为user+sys和real之间的时间差是IO

那么这一切意味着什么呢?这并不能证明我的答案是正确的,因为对于你为什么会看到自己的行为,可能还有其他解释。但它确实展示了用户计算时间、内核计算时间和我所说的做IO的时间之间的区别

下面是关于/dev/urandom和/dev/random之间区别的来源:

更新2:

我想我应该尝试解决Matthew的建议,即二级缓存未命中可能是问题的根源。核心i7有一个64字节的缓存线。我不知道你对缓存了解多少,所以我会提供一些细节。当您从内存中请求一个值时,CPU不会只得到那个值,而是得到它周围的所有64个字节。这意味着,如果您以一种非常可预测的模式访问内存,比如说数组[0]、数组[1]、数组[2]等等,则需要一段时间才能获得值0,但随后是1、2、3、4。。。速度要快得多。直到到达下一个缓存线,也就是说。如果这是一个整数数组,则0将变慢,1..15将变快,16将变慢,17..31将变快,以此类推

为了验证这一点,我制作了两个程序。它们都有一个包含1024*1024个元素的结构数组。在一种情况下,结构中有一个双精度,在另一种情况下,结构中有8个双精度。double的长度为8字节,因此在第二个程序中,我们正在以最糟糕的方式访问内存以获取缓存。第一个将很好地使用缓存

#include <stdio.h>
#include <stdlib.h>
#define MANY_MEGS 1048576
typedef struct {
    double a;
} PartialLine;
int main()
{
    int i, j;
    PartialLine* many_lines;
    int total_bytes = MANY_MEGS * sizeof(PartialLine);
    printf("Striding through %d total bytes, %d bytes at a time\n", total_bytes, sizeof(PartialLine));
    many_lines = (PartialLine*) malloc(total_bytes);
    PartialLine line;
    double x;
    for(i = 0; i < 300; i++)
    {
        for(j = 0; j < MANY_MEGS; j++)
        {
            line = many_lines[j];
            x = line.a;
        }
    }
    return 0;
}
这是一个大结构的程序,它们每个占用64字节的内存,而不是8字节

#include <stdio.h>
#include <stdlib.h>
#define MANY_MEGS 1048576
typedef struct {
    double a, b, c, d, e, f, g, h;
} WholeLine;
int main()
{
    int i, j;
    WholeLine* many_lines;
    int total_bytes = MANY_MEGS * sizeof(WholeLine);
    printf("Striding through %d total bytes, %d bytes at a time\n", total_bytes, sizeof(WholeLine));
    many_lines = (WholeLine*) malloc(total_bytes);
    WholeLine line;
    double x;
    for(i = 0; i < 300; i++)
    {
        for(j = 0; j < MANY_MEGS; j++)
        {
            line = many_lines[j];
            x = line.a;
        }
    }
    return 0;
}
#include <stdio.h>
int main()
{
    FILE * random;
    random = fopen("/dev/urandom", "r");
    int i;
    for(i = 0; i < 10000000; i++)
    {
        fgetc(random);
    }
    return 0;
}
mike@computer:~$ time ./waste_sys
real    0m1.138s
user    0m0.090s
sys     0m1.040s
mike@computer:~$ 
#include <stdio.h>
int main()
{
    FILE * random;
    random = fopen("/dev/random", "r");
    int i;
    for(i = 0; i < 100; i++)
    {
        fgetc(random);
    }
    return 0;
}
mike@computer:~$ time ./waste_io
real    0m41.451s
user    0m0.000s
sys     0m0.000s
mike@computer:~$ 
#include <stdio.h>
#include <stdlib.h>
#define MANY_MEGS 1048576
typedef struct {
    double a;
} PartialLine;
int main()
{
    int i, j;
    PartialLine* many_lines;
    int total_bytes = MANY_MEGS * sizeof(PartialLine);
    printf("Striding through %d total bytes, %d bytes at a time\n", total_bytes, sizeof(PartialLine));
    many_lines = (PartialLine*) malloc(total_bytes);
    PartialLine line;
    double x;
    for(i = 0; i < 300; i++)
    {
        for(j = 0; j < MANY_MEGS; j++)
        {
            line = many_lines[j];
            x = line.a;
        }
    }
    return 0;
}
mike@computer:~$ time ./cache_hits
Striding through 8388608 total bytes, 8 bytes at a time
real    0m3.194s
user    0m3.140s
sys     0m0.016s
mike@computer:~$
#include <stdio.h>
#include <stdlib.h>
#define MANY_MEGS 1048576
typedef struct {
    double a, b, c, d, e, f, g, h;
} WholeLine;
int main()
{
    int i, j;
    WholeLine* many_lines;
    int total_bytes = MANY_MEGS * sizeof(WholeLine);
    printf("Striding through %d total bytes, %d bytes at a time\n", total_bytes, sizeof(WholeLine));
    many_lines = (WholeLine*) malloc(total_bytes);
    WholeLine line;
    double x;
    for(i = 0; i < 300; i++)
    {
        for(j = 0; j < MANY_MEGS; j++)
        {
            line = many_lines[j];
            x = line.a;
        }
    }
    return 0;
}
mike@computer:~$ time ./cache_misses
Striding through 67108864 total bytes, 64 bytes at a time
real    0m14.367s
user    0m14.245s
sys     0m0.088s
mike@computer:~$ 
#include <stdio.h>
#include <stdlib.h>
typedef struct {
    double numbers[512];
} WholePage;
int main()
{
    int memory_ops = 1024*1024;
    int total_memory = memory_ops / 2;
    int num_chunks = 8;
    int chunk_bytes = total_memory / num_chunks * sizeof(WholePage);
    int i, j, k, l;
    printf("Bouncing through %u MB, %d bytes at a time\n", chunk_bytes/1024*num_chunks/1024, sizeof(WholePage));
    WholePage* many_pages[num_chunks];
    for(i = 0; i < num_chunks; i++)
    {
        many_pages[i] = (WholePage*) malloc(chunk_bytes);
        if(many_pages[i] == 0){ exit(1); }
    }
    WholePage* page_list;
    WholePage* page;
    double x;
    for(i = 0; i < 300*memory_ops; i++)
    {
        j = rand() % num_chunks;
        k = rand() % (total_memory / num_chunks);
        l = rand() % 512;
        page_list = many_pages[j];
        page = page_list + k;
        x = page->numbers[l];
    }
    return 0;
}
mike@computer:~$ time ./page_misses
Bouncing through 2048 MB, 4096 bytes at a time
real    2m1.327s
user    1m56.483s
sys     0m0.588s
mike@computer:~$