1介绍

gpuMagic包是专为ultilizing现代GPU的计算能力。包有能力编译R GPU内核代码和实现代码。因此,它很容易为用户设计自己的算法在纯R语言和运行在GPU的代码。包使用openCL作为其并行语言还支持所以并行使用CPU。

1.0.1程序设置

首先,它总是一个好主意来检查你的GPU avalability。

getDeviceList() # >没有找到设备,请确保计算机有一个图形卡或驱动程序已经正确安装。# >提示:# >为CPU,您可以安装英特尔/ ATI的英特尔的图形驱动程序/ AMD CPU分别。# >的GPU,你需要从你的供应商的网站下载图形驱动程序。# >没有设备被发现!# >零

上面列出的设备显示您当前的主要设备,如果你无法看到设备,你应该安装openCL司机获得相应的自动售货机。默认的设备装置1,如果您有多个设备,您可以查询和改变你的设备

getDeviceInfo(1) # >没有找到设备,请确保计算机有一个图形卡或驱动程序已经正确安装。# >提示:# >为CPU,您可以安装英特尔/ ATI的英特尔的图形驱动程序/ AMD CPU分别。# >的GPU,你需要从你的供应商的网站下载图形驱动程序。# >没有设备被发现!# >零setDevice(1) # >没有设备被发现!# >零

包还有一个GPU资源经理。你可以在任何时间检查内存使用量

gpuMagic.getMemUsage() # >设备内存使用量:# >——使用:(%)——总:

适当的设备已经被选择后,你准备写GPU代码。

# # #警告如果使用Nvidia图形卡,这是一个很好的练习来改变你的超时检测和恢复(TDR)设置的驱动程序将重置显卡如果函数需要太长的时间来运行代码。默认设置为2秒,这对于大多数操作太短。如果您使用的是另一个设备,如英特尔图形卡,你是安全的继续。

设置可以在NVIDIA Nsight找到。改变它更多,比如20秒将使大多数程序工作。

1.0.2中GPU代码

GPU的代码应该写成一个R功能和应兼容酸式焦磷酸钠函数。这是矩阵乘法的一个例子

# B C = % * % #每次C的函数计算列matMul <函数(印第安纳州,A, B) {C = % * % B[,]回报(C)}

你可以测试你的函数使用酸式焦磷酸钠函数

n = 10 m = 50 k = 100 =矩阵(runif (n * m), n, m) B =矩阵(runif (m * k), m, k) C_sapply =酸式焦磷酸钠(1:k, matMul, A, B) #内部矩阵运算函数B R C_internel = % * % #计算误差马克斯(abs (C_sapply-C_internel)) # [1] 0

正如你看到的,结果从酸式焦磷酸钠是一致的结果从R内部矩阵乘法函数。在GPU中运行上面的函数,它可以简单地通过改变酸式焦磷酸钠gpuSapply

C_gpu = gpuSapply (1: k, matMul, A, B) # >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠#计算误差马克斯(abs (C_gpu-C_internel)) # [1] 0

这是所有您需要做的运行GPU代码!我们可以试着更大的样本量GPU和R之间的速度差异的代码。

n = 1000 = 1000 k = 1000 =矩阵(runif (n * m), n, m) B =矩阵(runif (n * m), m, k)系统。时间(gpuSapply (1: k, matMul, A, B)) # >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠# >用户系统运行# > 1.266 0.012 1.278系统。时间(% * % B) # >用户系统运行# > 0.795 0.000 0.795

在我的测试环境(GF 940),上面的代码需要大约0.1秒和CPU (i5 - 6300 u) 0.6秒完成,6次在矩阵运算加速!

2已知问题和成功的钥匙

这里我们讨论一些常见的问题,你会在使用包。有些是由于openCL语言的局限性,有些是由于lazyness包的作者。

# # openCL的重要笔记+所有对象要么是一个标量或矩阵,向量是不支持+NA值不支持,我没有对他们进行测试,但应该是可以通过他们的设备,它可能不会像您预期的那样工作。此功能将被添加在不久的将来。在for循环+不分配(创建)变量或if指令,它可能导致一个意想不到的错误。请分配前循环或if指令。+小心当你自我作业,这样的代码一个[1:2]= [2:1]opencl以来不工作,没有一个临时空间来存储中间结果,因此作业将excuted序列,相当于是上面的代码[1]=[2],一个[2]= [1]。的价值一个[2]将是不正确的。+节省内存空间,函数参数由所有线程共享。因此改变函数参数的值是贬值,除非改变由一个线程不会被其他线程读。+如果你发现任何错误在运行openCL的方案,防止你代码,一个临时的解决方案是使用标量施工,因为大多数bug引入的可怜的设计矩阵的操作。# #动态分配?不!在R语言,它是相当容易提前创建一个矩阵不知道它的大小,例如,

n =样本(1:10,1)=矩阵(0 n, n)

将创建一个n×n矩阵A的大小是不确定的,直到评估的第一行代码。创建一个矩阵的能力在运行代码之前不知道它的大小称为动态分配。不幸的是,由于语言的限制,openCL不支持这个功能,这意味着每个矩阵的大小openCL代码之前应该知道其承办。因此,它是不可能动态地创建一个矩阵。为了解决这个问题,包提供了两种替代方式dynamic-like矩阵。

2.0.1使用宏

gpuSapply函数有一个参数.macroParms指定宏变量。因此,宏的值将插入函数,编译器而闻名。例如

dynamic_example1 <函数(印第安纳州,n) {B = C矩阵(0,n) = A + B返回(C)} n = 10 =矩阵(runif (n), n) #下面的代码不工作,# n的值是未知的在编译阶段# res_device = gpuSapply (1 dynamic_example1, n) #作品,n的值作为宏观knwown在编译阶段res_device = gpuSapply (dynamic_example1 1, n。macroParms = c (“n”)) # >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠

请注意,当您指定一个变量作为一个宏,你不能改变它在函数的值,因为它是固定的

2.0.2使用引用函数

如果你需要一个动态功能在您的代码中,您可以使用参考函数来创建它。引用函数就像一个宏。例如

#执行的第n个元素的列和每列的dynamic_example2 <函数(印第安纳州,n){#动态构造子集的第n个元素的印第安纳州列矩阵#几乎相当于A_dyn = [1: n,印第安纳州]# A_dyn未知的大小在编译阶段由于n的值是未知的A_dyn = subRef (A, 1: n,印第安纳州)C =总和(A_dyn)返回(C)} n = 10 M = 100 =矩阵(runif (M), M, M) res_device = gpuSapply (dynamic_example2, 1: M, n) # >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠res_cpu =酸式焦磷酸钠(dynamic_example2, 1: M, n) #检查误差范围(res_device-res_cpu) # > [1] 0 0

引用函数,你可以分配一个大矩阵然后它来创建动态子集矩阵与你需要的大小。然而,你需要小心当你在引用对象的操作。在上面的示例中,对引用对象的任何更改A_dyn将导致其目标对象的变化一个。请参考函数列表来查看可用的引用函数。

# #动态返回由于动态分配是不可能的,你可能想知道如果返回一个对象与动态尺寸是可能的。答案是肯定的但它贬值。如果你写多个返回代码中的函数。包将首先计算返回变量的最大长度,然后根据最大长度分配内存。如果一个返回变量的长度不能正确地推导出,你会得到一个警告消息,该变量的长度将被忽略。因此,为了正确地返回一个变量的不确定的长度,您应该创建一个矩阵大小总是大于返回变量,然后返回这个矩阵。因此编译器将总是有足够的空间为您返回变量(但与冗余空间)。例如:

dynamic_return <函数(印第安纳州,n){#动态构造子集的第n个元素的印第安纳州列矩阵#相当于A_dyn = [1: n,印第安纳州]A_dyn = subRef (A, 1: n,印第安纳州)#返回A_dyn,大小是未知的回报(A_dyn) #变量A_dyn tmp =矩阵的最大长度(0,nrow (A)) #告诉编译器返回的大小应该至少一样的变量tmp返回(tmp)} n = 10 M = 20 =矩阵(runif (M * M), M, M) #期望一个警告消息:待定返回大小已经发现suppressWarnings ({res_device = gpuSapply (dynamic_return, 1: M, n)}) # >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠#检索结果(第n行)res_device = res_device [1: n] #检查误差范围(res_device-A [1: n]) # > [1] 0 0

在未来,计划将提供一种更简洁的方式指定返回的大小。

# #行动两个矩阵之间具有不同的尺寸吗?没有实现。中使用的语言包只是R语言的一个子集,它完全支持标量标量,矩阵,矩阵,标量矩阵,矩阵标量。然而,矩阵的矩阵运算,它们必须具有相同的大小,否则将得到一个错误(例如。矩阵+向量是不允许的)。另一种解决方案是使用矩阵向量操作扫描功能,请参阅R文档使用。

# #错误检查由于性能的原因,只包执行错误检查在编译阶段。因此,当一个错误发生在GPU,默默地代码将继续运行,事故R在某种程度上没有任何提示。它总是一个好主意的示例代码酸式焦磷酸钠函数检查可能的错误在你启动摇臂与宝贵的数据。

# #序列变量序列变量,您可以创建没有限制。如你所见dynamic_example2函数,隐式创建一个序列变量1:n的大小是未知的。您可以使用函数seq:创建序列。唯一的例外是你不能改变的值序列变量,因为变量是一个紧凑格式的,不自己的记忆。如果你想创建一个序列变量在内存中,另一种方法是首先创建序列,并将其分配给一个新的变量,那么编译器将为新变量分配内存。

# #调用另一个用户定义的函数内部的R R当前版本不支持调用用户定义的函数功能在你的主要功能,此功能正在开发中,将会在不久的将来。

#更多的例子

这里我们提供更多的例子来展示包的使用。请注意,并不是所有的功能将有效运行在GPU上时,有可能是CPU的代码可以快于GPU。根据工作类型,你应该决定哪些设备你想运行在通过调用setDevice ()

# #找到最大的指数k值的每一列

largestK <函数(印第安纳州,k){#得到一列A_col = subRef(,印第安纳州)#最大的k值和指标A_col largest_value =矩阵(0 k) largest_ind =矩阵(0 k) #循环A_col找到最大的k值(我在1:长度(A_col))(在1:j k)的{{如果(A_col[我]> largest_value [j]){如果(j ! = k){#向后作业,以避免自我分配问题#(参见:重要提示)ind_src = (k - 1): j largest_value [ind_src + 1] = largest_value [ind_src] largest_ind [ind_src + 1] = largest_ind [ind_src]} largest_value [j] = A_col[我]largest_ind [j] =我打破}}}return_nocpy (largest_ind)} N = 1000 = 1000 k = 10 =矩阵(runif (N * M), N, M) #预热warm_res = gpuSapply (= 1: M, largestK, k = k。macroParms =“k”) # >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠warm_res =酸式焦磷酸钠(= 1:M, largestK, k = k) #计算时间system.time ({res_device = gpuSapply (= 1: M, largestK, k = k。macroParms = " k ")}) # >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠# >用户系统运行# > 0.877 0.004 0.880 system.time ({res_cpu =酸式焦磷酸钠(= 1:M, largestK, k = k)}) # >用户系统运行# > 0.873 0.156 1.029 #检查误差范围(res_device-res_cpu) # > [1] 0 0

# #蛮力方法找到的解决方案与L1损失函数线性回归

computeLoss <函数(印第安纳州,x, y,改){#找到线程的参数及parm = parm印第安纳州,#计算y的帽子,用不可复制的转置y_hat = x % * % t_nocpy(改)#绝对损失值(L1)损失损失=总和(abs (y-y_hat))返回(亏损)}#样本大小n = 1000 #的参数p = 2β= c(2,3) #生成数据矩阵x = (runif (n * p), n, p) e = runif (n) y = x % * %β+ e #蛮力搜索#真正的范围和精度search_range = seq(0.1 0, 10日)改= expand.grid (search_range search_range) #预热warm_res = gpuSapply (seq_len (nrow(改)),computeLoss, x = x, y = y,改=改)# >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠warm_res =酸式焦磷酸钠(seq_len (nrow(改)),computeLoss, x = x, y = y,改=改)#计算时间system.time ({res_device = gpuSapply (seq_len (nrow(改)),computeLoss, x = x, y = y,改=改)})# >没有设备被发现!汽车调度gpuSapply函数酸式焦磷酸钠# >用户系统运行# > 0.939 0.000 0.939 system.time ({res_cpu =酸式焦磷酸钠(seq_len (nrow(改)),computeLoss, x = x, y = y,改=改)})# >用户系统运行# > 0.970 0.000 0.971 #打印最小化损失函数的参数及parm [which.min (res_device)] # > Var1 Var2 # > 3560 2.4 - 3.5及parm [which.min (res_cpu)] # > Var1 Var2 # > 3560 2.4 - 3.5

#支持功能包目前支持有限数量的函数,它们的使用相似但不承诺一样R功能。例如,下降论点(功能还不支持。因此,在当前阶段,最好是只调用函数的简单的形式。当前可用的函数:

函数 请注意
nrow 可以用作维参数的值来创建一个矩阵
ncol 可以用作维参数的值来创建一个矩阵
长度
矩阵
( 下降不工作
腹肌
地板上
天花板
t
总和 仅支持一个参数
rowSums 仅支持一个参数
colSums 仅支持一个参数
rowMeans 仅支持一个参数
colMeans 仅支持一个参数
排序 只支持一个论点,升序排序
扫描
+
- - - - - -
*
/
^
>
> =
<
< =
= =
% * %
返回
显式声明函数 请注意
subRef 创建一个引用对象的一个子集矩阵,使用是一样的(函数
seq 支持从,,,length.out参数只
:
矩阵 声明一个矩阵(贬值)
标量 声明一个标量(贬值)

除了传统的R函数,包还提供了一些不可复制的方法。这些方法不分配内存的结果。相反,他们返回一个指示物(即指针)指向目标矩阵。因此,引用对象的任何更改将导致目标矩阵的变化。这个功能是为了节省内存的使用,但它可以是非常有用的,当你需要动态分配。你可以先分配一个矩阵与固定尺寸,然后创建一个引用对象指向内存空间,引用对象的大小可以确定在编译阶段。

函数 请注意
subRef 没有复制构造子集,使用是一样的(
t_nocpy 不可复制的转置,它返回一个引用一个矩阵的转置
return_nocpy 没有复制返回,返回的变量没有额外的副本,它不支持引用对象

# R版本和会话信息平台信息用来编译这个故事。

版# > _ # # > >平台x86_64-pc-linux-gnu拱x86_64 os linux-gnu # # > >系统x86_64, linux-gnu # >状态# >主要4 # >小1.1 # # > > 2021年08月# >第十天# > svn转速80725 R # # >语言>版本。字符串R版本以下4.4.1(2021-08-10)# >昵称踢东西sessionInfo() #平台以下4.4.1 (2021-08-10)# > > R版本:x86_64-pc-linux-gnu(64位)# >下运行:Ubuntu 20.04.3 LTS # > # >矩阵产品:默认# >布拉斯特区:/home/biocbuild/bbs - 3.14 - bioc / R / lib / libRblas。所以# > LAPACK: /home/biocbuild/bbs - 3.14 - bioc / R / lib / libRlapack。所以# > # >语言环境:# > [1]LC_CTYPE = en_US。utf - 8 LC_NUMERIC c# = >[3]而= en_GB LC_COLLATE = c# > [5] LC_MONETARY = en_US。utf - 8 LC_MESSAGES = en_US。utf - 8 # > [7] LC_PAPER = en_US。utf - 8 LC_NAME c# = > [9] LC_ADDRESS = C LC_TELEPHONE = C # > [11] LC_MEASUREMENT = en_US。utf - 8 LC_IDENTIFICATION = C # > # >附加基本包:# >[1]统计图形grDevices跑龙套数据集方法基础# > # >其他附加包:# > [1]gpuMagic_1.10.0 BiocStyle_2.22.0 # > # >加载通过名称空间(而不是附加):# > [1]Rcpp_1.0.7 pryr_0.1.5 bslib_0.3.1 # > [4] compiler_4.1.1 BiocManager_1.30.16 jquerylib_0.1.4 # > [7] class_7.3-19 tools_4.1.1 boot_1.3-28 # > [10] digest_0.6.28 rootSolve_1.8.2.3 jsonlite_1.7.2 # > [13] evaluate_0.14 lattice_0.20-45 rlang_0.4.12 # > [16] Matrix_1.3-4 rstudioapi_0.13 yaml_2.2.1 # > [19] mvtnorm_1.1-3 expm_0.999-6 xfun_0.27 # > [22] fastmap_1.1.0 e1071_1.7-9 stringr_1.4.0 # > [25] knitr_1.36 sass_0.4.0 DescTools_0.99.43 # > [28] gld_2.6.2 grid_4.1.1 data.table_1.14.2 # > [31] R6_2.5.1 lmom_2.8 rmarkdown_2.11 # > [34] bookdown_0.24 magrittr_2.0.1 codetools_0.2-18 # > [37] htmltools_0.5.2 BiocGenerics_0.40.0 MASS_7.3-54 # > [40] Exact_3.0 Deriv_4.1.3 stringi_1.7.5 # > [43] proxy_0.4-26