-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path数据处理效能的衡量.qmd
87 lines (60 loc) · 6.55 KB
/
数据处理效能的衡量.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 数据处理效能的衡量
效能是指使用行为目的和手段方面的正确性与效果方面的有利性。衡量数据处理效能的依据是计算效率、实现效果和获得效益,是对数据科学过程的效率、效果的概括性、综合性评价。总体来说,可以通过时间和空间两个维度对数据处理效能进行评价。一般来说,在保证结果真实准确的前提下,数据处理的时间越短、使用空间越少,那么数据处理的效能就越高。
## 时间衡量
任意特定操作、函数或一组代码的执行都需要一定的时间,在保证代码能够正确工作前提下,应该对R代码进行优化,使其执行得更快。在R中,对代码的时间进行衡量非常重要,因为这个步骤可以获知代码中运行缓慢和低效的部分,从而识别瓶颈,然后进行代码优化,进而提高程序的性能。因此,如何在R中对代码进行计时,对开发者来说是一项非常重要的技能。
在R中,能够实现计时的工具非常多,本书不会穷举所有的方法,而是对最常见而实用的方法进行介绍。这里我们统一使用`Sys.sleep`来构造测试代码,`Sys.sleep`是系统里面用于暂停制定时间的函数,可以让脚本运行暂停一定的秒数。首先,如果想要对某一段代码运行的时间进行衡量,可以使用**tidyfst**包的`pst`函数:
```{r}
library(tidyfst)
pst({
Sys.sleep(0.5) # 暂停0.5秒的时间
})
```
在上面给出的结果中,首先给出的是实际系统运行时间(Wall-Clock Time),随后在括号内给出的时间是CPU运行时间(CPU Time)。CPU运行时间指的是CPU实际用于处理某个程序的时间。这个时间只计算CPU在执行这个特定程序或进程上的工作时间,不包括系统处理其他任务的时间(如输入/输出操作、磁盘操作或网络通信)的时间。实际系统运行时间是从程序开始到程序结束所经过的总时间,就像是用墙上的时钟来度量时间一样。这个时间包括了所有的等待和延迟时间,比如CPU切换到其他任务、数据从硬盘加载、网络延迟等。因此,这个时间通常会比CPU运行时间长,因为它包括了程序执行中所有可能的等待时间。
有的时候,我们认为仅仅利用一次运行的计时结果不够稳定,可以增加运行的次数。在这种情况下,使用**microbenchmark**包的`microbenchmark`函数。比如我们想要让代码重复5次,可以这样操作:
```{r}
library(microbenchmark)
microbenchmark(Sys.sleep(0.1), # 暂停0.1秒的时间
times = 5) # 重复运行5次
```
得到的结果中,“Unit”部分声明了本次代码执行时间衡量的时间单位(“milliseconds”代表时间单位为微秒),*expr*代表执行的代码,*neval*代表代码执行的次数,而*mean*和*median*分别代表多次执行中时间的平均值和中位数,*min*和*max*则给出了执行时间的最小值和最大值。 此外,`microbenchmark`函数还可以比较不同代码的运行时间长短,实现方法如下:
```{r}
microbenchmark(Sys.sleep(0.1), # 暂停0.1秒的时间
Sys.sleep(0.2), # 暂停0.2秒的时间
times = 5) # 各自重复运行5次
```
## 空间衡量
一般来说,R将所有对象存储在计算机的物理内存(RAM,Random-access Memory)中进行操作。如果我们的计算机的物理内存不足以处理一些工作,那么就需要使用一些新的方法来对其进行处理。因此,了解计算环境的可用内存限制对我们而言至关重要。在使用R时值得注意的第一件事情是,您的计算机实际上有多少物理内存。通常情况下,我们可以通过查看操作系统的设置来了解这一点。举例来说,如果我们有一个内存为8GB的笔记本电脑,那么R可用的RAM量将远远小于此值,即上限是无法达到8G的。如果我们计划在这台笔记本上读入一个占用16GB内存的对象,那么我们需要换一台内存更大的计算机才有可能实现。
在R中,**pryr**包提供了一系列函数来对R中的内存使用情况进行分析。首先,我们可以使用`object_size`函数来测度某一个对象占用了多少内存:
```{r}
library(pryr)
a = rnorm(1e5) # 生成10万个服从正态分布的随机数,赋值给a
object_size(a) # 查看变量a占据了多少内存
```
如果我们想要看整个R当前所占的总内存数量,那么可以使用`mem_used`函数:
```{r}
mem_used()
```
有的时候,我们需要知道经过某一步操作之后,我们R占用内存的变化是多少,可以使用`mem_change`函数来完成这一步操作:
```{r}
mem_change(rm(a)) # 把a变量移除后,R所占内存数量的变化
```
注意,如果结果带有负号,说明R所占用内存减少了,否则就是增加了。
此外,如果对一个已经保存在本地的文件,需要查看其占用内存空间,可以使用**fs**包的`file_size`函数进行查看,只需要把文件的路径放入即可。
## 综合衡量
在上面的章节中,我们分别讲述了如何在R中对一些操作完成的时间和占用的内存进行测量。实际上,这两个步骤可以同时完成。使用**bench**包的`mark`函数可以实现这一过程,比如我们要对一段代码运行的时间和空间花销进行测度,可以这样操作:
```{r}
library(bench)
mark(a = rnorm(1e5))
```
上面的代码会对表达式执行若干次,然后进行效能的衡量。在返回结果中,*median*代表若干次迭代中时间花销的中位数,*mem_alloc*代表在运行表达式时,R分配的内存总量;而*itr/sec*则告诉我们每秒可以对该表达式执行多少次。 `mark`函数不仅可以对一个表达式的效能进行测量,还可以对多个表达式的效能进行比较。一般来说,默认情况下要求不同表达式的返回值必须是一致的,但是我们可以通过把*check*参数设置为FALSE来避免这一默认设置。实现方法如下:
```{r}
mark(
a = rnorm(1e5),
b = rnorm(1e5 + 1),
c = rnorm(2e5),
check = FALSE
)
```
以上代码利用`mark`函数对比了3个表达式的效能。
## 小结
在本章中,我们解释了什么是R语言中执行代码的效能,并给出一系列方法来对执行R代码时的时间和空间花销进行衡量。拥有这些工具,我们就可以较为精准地对R代码进行评估,看看其是否高效地完成了我们所需要的功能。