gpuMagic 1.10.0
gpuMagic包是专为ultilizing现代GPU的计算能力。包有能力编译R GPU内核代码和实现代码。因此,它很容易为用户设计自己的算法在纯R语言和运行在GPU的代码。包使用openCL作为其并行语言还支持所以并行使用CPU。
首先,它总是一个好主意来检查你的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秒将使大多数程序工作。
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次在矩阵运算加速!
这里我们讨论一些常见的问题,你会在使用包。有些是由于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矩阵。
的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函数酸式焦磷酸钠
请注意,当您指定一个变量作为一个宏,你不能改变它在函数的值,因为它是固定的
如果你需要一个动态功能在您的代码中,您可以使用参考函数来创建它。引用函数就像一个宏。例如
#执行的第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