matlab 提高效率,提高MATLAB程序的运行效率
在使用MATLAB的过程中,我对MATLAB的运行效率感到很头疼,就尝试了一些办法去提高之。现在把它们在这里作个总结,留作备忘和分享,之后有了新的想法也会补充进来。
使用矩阵运算替换循环语句
CPU并行计算
GPU并行计算
与C++协作
使用矩阵运算替换循环语句
这应该是老生常谈了;由于MATLAB处理矩阵挺方便的,所以一般也没人故意把矩阵运算拆成分量写。
有时候,for循环转成矩阵运算需要稍动一下脑筋。比如下面的例子中,plot_x是一个长度为size2的列向量,我们需要把n个plot_x连接起来,组成一个长长的列向量。
plot_x2 = zeros(size2 * n, 1);
for i = 1:n
plot_x2(((i - 1) * n + 1):(i * n), 1) = plot_x;
end
plot_x = plot_x2;
可以用这样的写法来替代:
plot_x = reshape(ones(n, 1) * (plot_x)', [size2 * n, 1]);
不知道这样效率能提高多少;就算不提高太多,看起来也更加令人心情愉悦。
不只是加减乘除,调用函数的时候也最好一次把所有的数据用矩阵喂给函数,而不是用for循环分开许多次调用。
例如,下面的例子中,plot_y是一个n行size2列的矩阵,plot_x是一个长度为size2的列向量,指示plot_y每一列画图时对应的横坐标。
for i = 1:n
plot(plot_x', plot_y(i, :));
end
可以修改为这样:
plot_x = ones(n, 1) * plot_x';
plot(plot_x, plot_y);
如果没记错的话,当时n大约取100,修改后执行时间从1分钟缩短到1秒。
有时候,从for循环到矩阵运算的转变需要用到arrayfun函数。需要先写一个用于处理单个元素(而不是矩阵)的函数,然后用arrayfun调用它。
下面的例子中,plot_x和plot_y的意义与上例相同,broken2是长度为size2的列向量,指示之前的程序在计算plot_y的每一列(也就是plot_x的每一行)时有没有出错(出错的点就不需要画出来了)。
plot_x = ones(n, 1) * plot_x';
for i = 1:size2
if ~broken(i)
plot(plot_x(:, i), plot_y(:, i));
end
end
可以改成这样:
plot_x = ones(n, 1) * (plot_x + arrayfun(@isbroken, broken))';
plot(plot_x, plot_y);
其中,isbroken是自己写的一个函数,用来处理broken的每一个元素,其定义为:
function a = isbroken(broken)
if broken
a=NaN;
else
a=0;
end
end
注意这里isbroken的形参broken所指并不是原来的broken,而是它的一个个分量。
把自己写的函数传给arrayfun时,必须在前面加上@,否则会报错。在很多其它场合是可加可不加的,比如使用ode45积分时,将微分方程传给ode45可以不加。
对于数据处理非常复杂的场合(比如积分),只要对矩阵中每一个元素的处理都是独立(互不影响)的,使用arrayfun改写,亲测都可以获得相当高的效率提升。
传给arrayfun的各个矩阵,要求各个维度的大小全部相同,arrayfun会把同一行、列位置上的元素传给同一个自定义函数;或者,一部分矩阵的各个维度大小相同,另一部分矩阵只有一个元素。
CPU并行计算
这个特别简单。
在命令窗口执行:
parpool(n)
这样,就在你的电脑上开了n线程的线程池,等会儿就可以使用parfor用这n个线程并行计算。我的CPU是四核八线程,我一般取n为4或者6。
parfor的用法和for差不多,只需要注意不同次循环之间的数据不能互相影响。举个例子:
parfor i = 1:s
c(i,:,:)=pcrpoint(sgm, r, b(i), n, ts, tu, t0, rand(3,1), apr, tol);
end
这样4线程或者6线程调用自己写的pcrpoint去计算了。
parpool和parfor还有一些其它功能,我没用过,这里不提。
GPU并行计算
MATLAB是原生支持GPU运算的,并且不需要编程者对GPU的结构、CUDA编程模型非常了解。使用gpuDevice命令可以看到自己GPU的一些参数。
MATLAB对GPU的支持有以下的限制:
必须是NVIDIA的显卡(支持CUDA),AMD的不行。
要求Compute Capability为2.0或更高。这个一般都能满足。
需要安装CUDA Toolkits,这个到NVIDIA官网下载,按照默认的选项安装就好了。
需要编程者有足够的耐心,这个不是在开玩笑。改写一些复杂的函数时,会相当相当麻烦。另外,GPU的整个环境特别容易出问题,更新一下显卡驱动都可能让整个环境崩溃,出问题后要有耐心地卸载掉重装CUDA Toolkits。
要使用GPU计算,需要先把数据放到显存上。如果已经有了一个矩阵a,可以这样把数据复制到显存上:
dev_a = gpuDevice(a);
或者直接在显存上创建矩阵:
dev_a = zeros(5, 5, 'gpuArray');
ones,rands等函数也可以类似地使用。
接下来的计算就是自然而然地发生在GPU上了。比如,可以计算:
dev_c = dev_b + dev_a;
dev_c = sin(dev_a);
加减乘除和常见的内置函数都可以用。具体哪些可用而哪些不可用,这里有详细的列表。
如果需要将显存上的数据复制到CPU内存中,可以使用gather函数。
除了使用内置的函数以外,还可以自己写一个处理单个元素的函数,然后使用arrayfun调用。要命的是,在自己写的那个函数中,不能有任何矩阵出现,必须全部是对单个元素的操作。
这是我写的一个例子:
[x(:, 1), y(:, 1), z(:, 1)] = arrayfun(@ode45_lrz_onlyResult_ew, t0_span, x0, y0, z0, rt_span, at_span, sgm_span, r_span, b_span);
其中,t0_span等参数已经被提前置于显存上。ode45_lrz_onlyResult_ew是我写的一个函数,对每个矩阵中的单个元素进行积分。它是这样定义的:
function [yout_x, yout_y, yout_z] = ode45_lrz_onlyResult_ew(tfinal, y0x, y0y, y0z, RelTol, AbsTol, sgm, r, b)
...
end
具体代码因为太长不贴出。截一小段图,你们大概就知道不许出现矩阵有多麻烦了。
Windows不允许GPU有两秒以上的时间无响应;所以,如果你交给GPU的计算任务太重,MATLAB会报错。在注册表中修改以下键值为0就可以取消这个限制:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\TdrLevel
如果没有这个键值,就新建一个。这样做的副作用是,GPU全力计算的时候电脑可能会卡住,等算完就好过来了。
到这一步,效率已经非常高了。我测试的结果是,同样的任务放到GPU上计算,MATLAB的效率可以达到C++的四分之一,并且相较于用C++写CUDA,学习成本低。
与C++协作
MATLAB支持与C++协作。我尝试过的有两种方式:调用C++写好的exe,或者调用C++写好的lib。前者兼容性好一些,后者传参方便一些。
举一个调用exe的例子。MATLAB端把param写入到'm2c.bin'里,然后启动exe并等待结束,再从'c2m.bin'里读返回的yout;
fclose('all');
fid = fopen('m2c.bin', 'wb');
fwrite(fid, param', 'double');
fclose(fid);
system('ode_solver_launcher.exe');
fid = fopen('c2m.bin', 'rb');
yout = fread(fid, [3, size2 * n], 'double');
C++端从"m2c.bin"里读取param,计算,然后将计算结果yout写入到"c2m.bin"里:
FILE *fin = fopen("m2c.bin", "rb"), *fout = fopen("c2m.bin", "wb");
fread(param, sizeof(double), 3 * size, fin);
...
fwrite(yout, sizeof(double), 3 * n * size, fout);
fflush(fout);
fcloseall();
这里使用二进制文件来传输数据。如果数据量小,也可以用文本文件来传输,MATLAB用fprintf、fscanf,C++用printf、scanf即可。不赘述。
再举一个调用dll的例子。
C++端写dll可以参考这里。需要注意的有几点:
按照教程的方法,最新版本vs2017创建项目时不应该选择“Win32控制台应用程序”而是“Windows Desktop Wizard”(Windows桌面应用程序向导),这样才会弹出下一步的框。
dll给MATLAB的接口必须是C(而不是C++)格式的。也就是说,在这个接口处,不能使用C++有而C没有的东西(比如bool),并且在源文件中引用申明导出函数的头文件时,需要告诉编译器按C的方式编译:
_EXTERN_C
#include "lrz_pcrgraph_gpu_c_dllept.h"
_END_EXTERN_C
_EXTERN_C和_END_EXTERN_C其实就是extern "C" {和}。
参数传递时不要使用高维数组(尽管MATLAB原则上支持高维数组)。我在尝试中发现高维数组会有问题。
MATLAB端则需要将param转为libpointer对象,并创建适当的libpointer对象yout_x容纳返回值,使用头文件'lrz_pcrgraph_dllept.h'加载'lrz_pcrgraph.dll',调用C++函数'pcrgraph'计算,卸载dll,将yout转化为矩阵,再进行下一步的处理。
yout_x = libpointer('doublePtr', zeros(s * n, 1));
param = libpointer('doublePtr', param);
if ~libisloaded('lrz_pcrgraph')
loadlibrary('lrz_pcrgraph.dll', 'lrz_pcrgraph_dllept.h');
end
calllib('lrz_pcrgraph', 'pcrgraph', yout_x, param);
unloadlibrary('lrz_pcrgraph');
yout_x = yout_x.value;
在上例中,C++中的double*对应MATLAB的doublePtr。具体的对应在这里可以看到。需要指出的是,实际测试时发现char*到int8Ptr或charPtr的对应有问题,可以使用unsigned char*到uint8Ptr来代替。
即使是调用已经编译好的dll,也需要装有C语言编译器,否则MATLAB会报错。
如果需要把程序放到别人电脑上计算,无论哪种方法,都需要注意:C++写的部分可能需要依赖别的dll才能运行。可以使用dumpbin工具检查exe或者dll依赖哪些其它的dll,然后把它们和刚刚写的程序放到同一个目录下;或者在编译前将Runtime Library编译选项改为/MT(默认为/MD),就可以把用到的别的dll中的函数在编译时就收纳到自己的程序里了。
matlab 提高效率,提高MATLAB程序的运行效率相关推荐
- matlab有无并行功能,使用MATLAB并行计算功能提高多核系统性能
使用MATLAB并行计算功能提高多核系统性能 2009-12-13 22:12:11| 分类:matlab相关| 标签:|字号大中小订阅 maxNumCompThreads 命令 由于处理器时钟频率的 ...
- 【程序员觉醒】提高效率,增加输出
[程序员觉醒]提高效率,增加输出 1. 提升专注力 2. 有效拆解和规划任务 3. 管理好时间 4. 善于总结,累积经验 5. 学习新知识,拓展知识面 6. 适当的休息和充足的睡眠 一直都觉得自己的程 ...
- 循环嵌套问题:为什么大循环在内,小循环在外可以提高程序的运行效率
很多人觉得,循环嵌套可以任意嵌套,无论循环次数的大小: 但是事实上并不是这样: 程序对于计算机来说,最终都会编译生成计算机可以识别的指令: 如今的计算机为了提高效率,指令执行机制都会存在预处理指令的分 ...
- 马克!程序员必须收藏的 10 类工具库,助你提高效率变大神
对程序员和IT专业10个有用的工具和库 橙色的扩音器挂在橙色的墙上"Oleg Laptev在Unsplash网站上写道. 每一个行业都有它自己的一些工具.软件行业也不例外.一个好的程序员比其 ...
- 多线程在任何情况下均能提高效率吗?
早段时间在网上看到一篇文章,其中就写了使用多线程模型实现文件的快速搜索.由此使我一直在考虑,多线程模型真的能够提高应用程序的效率吗?如果不能,那么多线程模型能干什么呢? 很多程序员一谈到提高应用程序效 ...
- Android 功耗(18)---android省电和提高效率
android省电和提高效率 一.Android省电开发之性能优化 我之前博客有在总结,just a little,http://blog.csdn.net/wtyvhreal/article/det ...
- 提高效率的Java代码优化小技巧
可以提高效率的Java代码优化小技巧 前言 代码优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面 ...
- 多线程为什么可以提高效率
JAVA多线程,真的能提高效率吗 举个栗子 比如挖一个隧道,有2种开工方法 1.只在山的一头挖,直至挖到山的另一头,从而打通隧道,这可以看成是单线程 2.在山的两头挖,同时开工,最后在山的中间接通,从 ...
- python提高效率(优化)的心得总结
转载自:python提高效率(优化)的心得总结 作者:Capricorn.python 用python也有小两年了,不是开发,所以代码应该没那么精湛.但是经常会写测试脚本和小工具.在积累了大量的库以后 ...
最新文章
- Android数据库专家秘籍(七)经验LitePal查询艺术
- 初识Mysql(part8)--我需要知道的5个关于计算字段的小知识
- 工作10以上老程序员都去哪了?作为新时代的程序员我们该何去何从
- 关于spring的事务管理(单数据库):纯属猜测。
- 【ResNet】Pytorch从零构建ResNet18
- c15语言中不支持十六进制的数据,从十六进制字符串中提取数据
- Android简历附件2
- 解决contenteditable内自动生成font标签问题
- 学生学籍管理系统(c语言)
- Qt开发技术:图形视图框架(二)场景QGraphicsScene、QGraphicsItem与QGraphicsView详解
- cs程序上传文件至web服务器,asp.net 程序上传到导服务器发生异常
- Android开发项目实战:实现折叠式布局,android组件化开发与sdk
- 2022-03-30 StackOverflowError与OutOfMemoryError详解
- NOTIFIER诺帝菲尔消防主机电源维修及日常维护
- 《Win10——常用快捷键》
- 一级计算机B理论知识和答案,计算机一级B试题+理论答案 10秋06
- Android 切换全屏,取消全屏
- 驾图车联网:区块链重塑汽车大数据的价值链和生态链
- (初学者)使用DOSBox编写汇编程序
- 电源常识-纹波-EMI