R语言并行编程
并行编程是一种软件开发实践,它涉及将计算或任务分解成更小的部分,这些部分可以并发或并行执行。通过利用计算机或集群中的多个处理器或核心,并行编程可以帮助提高 R 代码的性能和效率。并行编程的主要概念是,如果使用单个处理器在一个操作中需要 S 秒,那么当涉及 N 个处理器时,它应该能够在 S / N 秒内执行。
R语言并行编程的必要性
大多数情况下,R 代码在单个核心上运行速度很快。但有时操作可能会:
消耗过多的 CPU 时间。
占用过多的内存空间。
读取或写入磁盘花费的时间过长。
传输时间过长。
隐式并行性
R 提供了强大的库支持。有时,我们甚至在不知不觉中就进行了并行编程。这是因为现在的 R 提供了具有内置并行性的库,我们可以在后台使用它们。这种隐式并行性提高了我们的编程效率。但是了解实际发生的事情(即使是在幕后)也是很好的。
让我们考虑一个隐式并行性的例子
并行基本线性代数子程序 (BLAS)
基本线性代数子程序 (BLAS) 库是为特定类型的 CPU 在 R 中自定义编码的,目的是利用芯片组架构的优势。拥有优化的 BLAS 总是很有益的,因为它可以提高执行性能。
容易并行化的并行性
容易并行化的并行性是统计学和数据科学中的一种常见方法。它能够解决数据科学和统计学中的许多问题。在这种类型的并行性中,问题被分成多个独立的部分,并且所有部分都被同时执行,因为它们彼此之间没有任何联系。
语法
可以使用 `lapply()` 函数在 R 中实现容易并行化的并行性。此函数具有以下语法:
lapply(list, function)
示例
它接受一个列表和一个函数。它返回一个列表,其长度等于输入列表。让我们考虑一个程序来说明此函数的工作原理:
# Creating a list myList <- list(data1 = 1:5, data2 = 10:15) # Use lapply() function and # calculate the mean lapply(myList, mean)
输出
$data1 [1] 3 $data2 [1] 12.5
正如您在输出中看到的,已经显示了列表元素的平均值。
`lapply()` 函数的工作方式类似于循环,我们迭代列表的每个元素并将函数应用于它。
现在让我们更深入地了解实际发生的情况:
我们逐一迭代每个元素,这就是为什么当我们将函数应用于列表的单个元素时,其他元素只是在内存中空闲。我们可以在 R 中并行化这件事。主要思想是将列表对象分成多个处理器,然后我们可以同时将函数应用于列表的所有子集。
因此,我们可以使用以下步骤实现并行性:
将列表分解到多个处理器中。
将提供的函数克隆到多个处理器中。
同时将函数应用于多个核心。
将来自多个核心的结果组合到单个列表中。
显示结果。
R中的并行编程包
R 中的 `parallel` 包随 R 的安装一起提供。此包结合了 R 中的两个包:`snow` 和 `multicore`。
`parallel` 包专门用于以并行方式将任务交付给每个核心。具体来说,它是通过 `mclapply()` 函数执行的。`mclapply()` 函数类似于 `lapply`,但前者能够将任务分配给多个处理器。`mclapply()` 函数还收集函数调用的结果,将它们组合起来,并将其结果作为长度与原始列表相同的列表返回。请注意,R 允许我们使用 `detectCores()` 函数,我们可以用它来获取系统中存在的核心数量。
让我们考虑以下程序,它说明了 `mclapply()` 函数的工作原理:
注意 - 请注意,`mc.cores` 的值大于 1 仅在非 Windows 操作系统中有效。因此,以下代码是在 Windows 之外的操作系统中执行的。
示例
# Import library library(parallel) library(MASS) # Creating a list myList <- list(data1 = 1:10000000, data2 = 1:100000000) cat("The estimated time using lapply() function:
") # Calculate the time taken using lapply system.time( results <- lapply(myList, mean) ) # Get the number of cores numberOfCores <- detectCores() cat("The estimated time using clapply() function:
") # Calculate the time taken using lapply() using mclapply() system.time( results <- mclapply(myList, mean, mc.cores = numberOfCores) )
输出
The estimated time using lapply() function: user system elapsed 0.40 0.00 0.43 The estimated time using clapply() function: user system elapsed 0.12 0.00 0.17
您可以在输出中看到使用 `apply()` 和 `mcapply()` 函数时的差异。
使用 foreach 和 doParallel 包进行并行编程
现在我们将看到如何使用 R 中的 `foreach` 库实现并行编程。但在进入正题之前,让我们看看 R 中基本的 for 循环是如何工作的:
示例
# Iterate using the for loop from 1 to 5 # And print the square of each number for (data in 1:5) { print(data * data) }
输出
[1] 1 [1] 4 [1] 9 [1] 16 [1] 25
正如您在输出中看到的,从 1 到 5 的每个数字的平方都显示在控制台上。
Foreach 包
现在让我们谈谈 `foreach` 包和方法。`foreach` 包为我们提供了 `foreach()` 方法,我们可以用它轻松实现并行编程。
语法
如果您尚未在系统中安装 `foreach` 库,请在 CRAN 的终端中使用以下命令:
install.packages("foreach")
`foreach` 方法类似于基本的 for 循环方法,但前者使用 `%do%` 运算符,这意味着运行特定类型的表达式。两者在返回数据结构方面也有所不同。
示例
考虑以下程序,它说明了 `foreach` 方法的工作原理:
# Import foreach library library(foreach) # Iterate using the foreach loop from 1 to 5 # And print the square of each number foreach (data=1:5) %do% { data * data }
输出
[[1]] [1] 1 [[2]] [1] 4 [[3]] [1] 9 [[4]] [1] 16 [[5]] [1] 25
正如您在输出中看到的,从 1 到 5 的每个数字的平方都显示在控制台上。
doParallel 包
`doParallel` 包为我们提供了 `%dopar%` 运算符,我们可以将其与 `foreach` 一起使用。通过将此运算符与 `foreach` 一起使用,我们将能够为每次迭代使用不同的处理核心。您可以使用 CRAN 中的以下命令下载“doParallel”包:
install.packages("doParallel")
示例
现在让我们考虑以下程序,它演示了 `foreach` 方法以及 `%dopar%` 运算符的工作原理:
# Import foreach library library(foreach) library(doParallel) library(MASS) # Get the total number of cores numOfCores <- detectCores() # Register all the cores registerDoParallel(numberOfCores) # Iterate using the for loop from 1 to 5 # And print the square of each number # Using parallelism foreach (data=1:5) %dopar% { print(data * data) }
输出
[[1]] [1] 1 [[2]] [1] 4 [[3]] [1] 9 [[4]] [1] 16 [[5]] [1] 25
从 1 到 5 的每个数字的平方都显示在控制台上。
结论
在本教程中,我们讨论了 R 中的并行编程。我们讨论了 `foreach` 和 `doParallel` 等库,可以使用它们在 R 中实现并行编程。我们还了解了 `mcapply()` 等函数的工作原理。并行编程是任何编程语言最重要的概念之一,我相信本教程肯定有助于在数据科学领域获得良好的知识。