【转载】让你的MATLAB运行效率更快一些吧!

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了【转载】让你的MATLAB运行效率更快一些吧!相关的知识,希望对你有一定的参考价值。

参考技术A

转自 https://www.digquant.com.cn/forum.php?mod=viewthread&tid=258

1、改变算法,多用矩阵运算(尤其是矩阵乘法),尽量减少for循环;

2、减少for循环中的函数调用;

传统观点认为for-loop是影响性能的致命环节,让我们来对此验证:

Elapsed time is 0.000239 seconds.

Elapsed time is 0.000050 seconds.

从上面的实验结果可以得出以下结论:

1、tic/toc语句的时间开销可以忽略不计
2、for-loop语句本身的时间开销也非常小,关键的影响效率的地方不在于循环本身,而是在于循环的内部。
3、tic/toc不一定要成对出现,一个tic后面可以有多个toc,但需要需要重新计时的时候,要再次执行tic。
4、toc的结果可以用变量接收下来,如:

接下来我们就借助for循环,分析一下其他的各个影响效率的因素。

内建函数

Mean elapsed time is 0.032866 seconds.

m-函数

Mean elapsed time is 0.185556 seconds.

匿名函数

Mean elapsed time is 0.561228 seconds.

内联函数

Mean elapsed time is 19.5606 seconds.

从上面的实验结果可以得出以下结论:
1、内联函数的调用时间开销最小,约为for-loop本身的10倍
2、m-函数的调用时间开销约为内联函数的6倍,约为for-loop本身的60倍
3、匿名函数的调用时间开销约为m-函数的3倍,约为for-loop本身的187倍
4、内联函数的调用时间开销过大,尽量不要在循环中使用
5、另外MEX-函数的调用时间开销,理应介于内联函数和m-函数之间

矩阵索引

Mean elapsed time is 0.007592 seconds.

Mean elapsed time is 0.007954 seconds.

Mean elapsed time is 0.663598 seconds.

Mean elapsed time is 0.273345 seconds.

Mean elapsed time is 0.730042 seconds.

Mean elapsed time is 1.00852 seconds.

Mean elapsed time is 0.009025 seconds.

Mean elapsed time > 20 minutes.

因此,如果不预先分配好内存,将会大大增加仿真时间,拖慢执行效率。

所幸的是,由于这个现象的重要性,Matlab的编辑器能够发现并提示这个问题,会用红的波浪线 ~ 标记出来。

向量化:

MATLAB向量化函数
accumarray函数
arrayfun函数
bsxfun函数
cellfun函数
spfun函数

A =

101
206
0
208

subs =
1 1 1
2 1 2
2 3 2
2 1 2
2 3 2

val =
101
102
103
104
105

1、val的元素个数与subs的行数是一致的。

2、 A = accumarray(subs, val) 的实现过程分成2步。

第一步
是把val中的元素,按照subs对应行所给出的下标放到一个新的cell矩阵B中(cell是为了方便解释,也就是说B矩阵中的每个位置可以放入多个数值),注意,subs的值是B的下标,不是val的。举例来说,subs第一行[ 1 1 1],意思就是把val中第一个元素(val(1))放入到B(1,1,1)的位置,依次类推,val(2)放入到B(2 1 2),val(3)放入到B(2 3 2),val(4)放入到B(2 1 2),val(5)放入到B(2 3 2)。此时,可以看到B(1,1,1)中有1个数(val(1));B(2 1 2)有2个数(val(2),val(4));B(2 3 2)也有2个数(val(3),val(5))。

第二步
把B中每个单元中的数分别累加,并放入到A的对应位置。

注: accumarray 默认的是把每个单元中的数累加,因为对每个单元中的数的默认处理函数是sum。可以通过 A = accumarray(subs,val,[],[@fun](https://github.com/fun "@fun")) 的调用格式来指定其他的处理函数,比如说mean。对指定的fun函数的要求是,接受列向量输入,输出单个的数值型,字符型或逻辑型变量。A的维数与B相同,A中的元素默认为零。A的大小为max(subs(1))×max(subs(2))×max(subs(3))…

很显然,A的维数与subs的列数相等。

例子:
1000人,身高分布在170 180cm,体重在110 100斤,年龄分布在20~50岁,计算身高体重都相等的人的年龄平均值。结果用矩阵来表示:行数表示身高,列数表示体重,矩阵元素表示年龄的平均值。

arrayfun函数实现的是将指定的函数应用到给定数组在内的所有元素。这样以前不可避免的循环现在可以向量化了。

生成一个这样的n×n矩阵

以前,当我们想对一个矩阵A的每一列或每一行与同一个向量a进行某些操作(比较大小、乘除等)时,只能用循环方法或者利用repmat函数将要操作的向量a复制成和A一样尺寸的矩阵,进而进行操作。从Matlab R2007a开始,有了更有效的方法,那就是bsxfun函数。
有如下矩阵:

向量为b=[1 2 3]T,请找出b在A矩阵列中的位置loc=[1,4]。

方法1:

方法2:

方法3:

方法4:

方法5:

A=‘Hello’, ‘MATLAB’, ‘I love MATLAB’, ‘MATLAB is powerful’, ‘MATLAB is the language of technical computer’;

cellfun( @length ,A)

ans =
5 6 13 18 44

</pre>

a =

(1,2) 1
(3,20) 2
(20,30) 3
(60,60) 4
(100,80) 5

</pre>

sa =
(1,2) 2
(3,20) 5
(20,30) 10
(60,60) 17
(100,80) 26

常用的预分配内存函数:

代码示例:一些简单技巧优化JavaScript编译器工作详解,让你写出高性能运行的更快JavaScript代码

告诉你一些简单的技巧来优化JavaScript编译器工作,从而让你的JavaScript代码运行的更快。尤其是在你游戏中发现帧率下降或是当垃圾回收器有大量的工作要完成的时候。

单一同态:

当你定义了一个两个参数的函数,编译器会接受你的定义,如果函数参数的类型、个数或者返回值的类型改变编译器的工作会变得艰难。通常情况下,单一同态的数据结构和个数相同的参数会让你的程序会更好的工作。

function example(a, b) {
  // 期望a,b都为数值类型
  console.log(++a * ++b);
};

example(); // 不佳
example(1); // 仍然不佳
example("1", 2); // 尤其不佳

example(1, 2); // 很好

展开:

编译器会在编译的时候求出变量的值并且展开它(最佳实践),因此在程序执行前可以尽量多的表达信息。常量和变量一样可以被展开,只要它们没有用任何的与运行期有关的运算。

const a = 42; // 很容易展开
const b = 1337 * 2; // 可以求值
const c = a + b; // 也可以求值
const d = Math.random() * c; // 只能展开‘c‘
const e = "Hello " + "Medium"; // 其他类型的值也可以

// 展开前
a;
b;
c;
d;
e;

// 展开后
// 会在编译的时候做好这些!
42
2674
2716
Math.random() * 2716
"Hello Medium"

函数内联:

JIT编译器会找出你的代码中哪些部分是经常执行的。在编译的时候通过将函数分成小块来将代码块内联并且热追踪函数之后代码会执行的更快。

// 以下这些会内联
// [?] 单一的返回语句
// [?] 返回总是一样的
// [?] 返回时单一同态的
// [?] 参数是单一同态的
// [?] 函数体是一个单一的语句
// [?] 不是包裹在另一个函数体内
// ...
function isNumeric(n) {
  return (
    n >= 48 && n <= 57
  );
};

let cc = "8".charCodeAt(0);

// 内联前
if (isNumeric(cc)) {

}

// 内联之后
if (cc >= 48 && cc <= 57) {

}

Declarations:

避免在频繁调用的函数里声明函数/闭包或对象。对象(也包括函数,对象)会被压到堆里,垃圾回收器会影响这个堆,那里有很多 wat和wut 需要确定下一步(像释放与否)

相反,声明一个变量会很快,因为它们是被压到栈里。比如,一个函数会有自己的栈,与函数相关的变量都会压到这个栈里,无论何时这个函数退出,栈也随着释放。

// 欠佳
function a() {
  // 决不再函数里面申明函数
  // 会在每次调用函数分配资源
  let doSomething = function() {
    return (1);
  };
  return (doSomething());
};

let doSomething = function() {
  return (1);
};

// 很好
// 在函数外申明 ‘doSomething‘
// 因此可以只调用它,而不是
// 在每次调用‘b‘去申明和调用
function b() {
  return (doSomething());
};

参数:

函数调用的代价是昂贵的(如果编译器不能内联它们)。尝试去使用尽可能少的参数并且不在函数体内修改参数。

function mul(a, b) {
  return (arguments[0]*arguments[1]); // 欠佳, 很慢
  return (a*b); // 很好
};

function test(a, b) {
  a = 5; // 欠佳, 不修改参数标识
  let tmp = a; // 很好
  tmp *= 2; // 可以修改伪造的 ‘a‘
};

数据类型:

尝试尽可能多的取用数值和布尔类型,它们在比较操作中比其他基本类型要快很多。比如,声明一个字符串类型会偷偷的造成一大堆垃圾数据,因为字符串是一个复杂的有很多预设属性的对象。

同时,避免操作负数和多位小数的双精度浮点数。

const ROBOT = 0;
const HUMAN = 1;
const SPIDER = 2;

let E_TYPE = {
  Robot: ROBOT,
  Human: HUMAN,
  Spider: SPIDER
};

// 欠佳
// 避免在大任务中缓存字符串(最好在一般中场景也不)
if (entity.type === "Robot") {

}

// 很好
// 编译器会算出数值表达式
// 没有深度计算会更快
if (entity.type === E_TYPE.Robot) {

}

// 完美
// 右侧的二元表达式也是可以被展开的
if (entity.type === ROBOT) {

}

严格和抽象运算符:

尝试使用三等号操作如“===”(严格的)而不是 “==” (宽松的, 抽象的)。执行严格的运算符保证编译器去预设一个明确的值,而不用以多种情况去比较语句。(比如 n>0=^true),这样会导致更好的性能方案。

严格条件:

JavaScript提供了很棒的语法糖来允许你比如“if (a) then bla”这样的代码。这种情况下,编译器必须去以多种类型去比较“a”来确定是否为真,因为它不知道是那种类型的结果。当然,你应该使用这些很棒的语法糖,但是在快速执行的复杂的有多个返回语句的函数(如 null或者Object)中,你应该考虑以下建议。

有毒:

以下列表里的语言特性,会较少或阻止代码优化过程。

  • eval

  • with

  • try/catch

对象:

对象实例通常会尝试共享一个隐藏的类,谨慎的给一个实例化的对象添加一个成员变量,因为这样会创建一个新的隐藏类并且对编译器来说这会复杂很多(对你也会一样)

// 隐藏类‘hc_0‘
class Vector {
  constructor(x, y) {
    // 编译器找到并且期望的成员声明在这
    this.x = x;
    this.y = y;
  }
};

// 两个vector对象共享隐藏类‘hc_0‘
let vec1 = new Vector(0, 0);
let vec2 = new Vector(2, 2);

// 欠佳,vec2这样会创建新的隐藏类‘hc_1‘
vec2.z = 0;

// 很好,编译器知道这个成员
vec2.x = 1;

循环:

缓存你的数组长度属性,并且使用数组作为一个单一同态的类型。通常避免使用“for..in”或者在数组上循环,因为这会很慢。

循环语句中continue 和 break 语句会比if语句体更快。保持你的循环干净,把所有的东西打包成一个子函数,这样编辑器感觉会更舒服。同时,使用预增操作符(_++ i 代替 i ++_)会有一点点的性能提升。

let badarray = [1, true, 0]; // 欠佳, 不要混合类型
let array = [1, 0, 1]; // 好的用法

// 不好的选择
for (let key in array) {

};

// 更好的
// 但是总是缓存数组大小
let i = 0;
for (; i < array.length; ++i) {
  key = array[i];
};

// 很好
let i = 0;
let key = null;
let length = array.length;
for (; i < length; ++i) {
  key = array[i];
};

写在最后FOR Freedom | 知识就应该被开放的获取,看看外边的世界,以及IT这一行,少不了去Google查资料,最后,安利一个V——PN代理。一枝红杏 VPN,去Google查资料是绝对首选,连接速度快,使用也方便。我买的是99¥一年的,通过这个链接(http://my.yizhihongxing.com/aff.php?aff=2509)注册后输上会员中心得优惠码,平摊下来,每月才7块钱,特实惠。

本文标签 JavaScript编译器 JavaScript优化 JavaScript技巧 JavaScript编译器原理

转自 SUN‘S BLOG - 专注互联网知识,分享互联网精神!

原文地址 : 代码示例:一些简单技巧优化JavaScript编译器工作详解,让你写出高性能运行更快的JavaScript代码

相关阅读:如何在程序开发项目中选择合适的 JavaScript 框架,节省时间和成本的9款极佳的JavaScript框架介绍
相关阅读:网站环境apache + php + mysql 的XAMPP,如何实现一个服务器上配置多个网站?

相关阅读:什么是工程师文化?各位工程师是为什么活的?作为一个IT或互联网公司为什么要工程师文化?

相关阅读: 对程序员有用:2017最新能上Google的hosts文件下载及总结网友遇到的各种hosts问题解决方法及配置详解

相关阅读:移动端UI设计越来越流行的高斯模糊(Gaussian blur)和毛玻璃效果(磨砂效果),如何使用Android RenderScript简单实现?

相关BLOG:SUN’S BLOG - 专注互联网知识,分享互联网精神!去看看:www.whosmall.com




以上是关于【转载】让你的MATLAB运行效率更快一些吧!的主要内容,如果未能解决你的问题,请参考以下文章

代码示例:一些简单技巧优化JavaScript编译器工作详解,让你写出高性能运行的更快JavaScript代码

转载让你的MATLAB代码飞起来

安装Windows10后,必做的6项优化,让你的电脑运行更快

用 PyPy 让你的 Python 代码运行得更快!

用 PyPy 让你的 Python 代码运行得更快!

数据库优化之让你的SQL运行得更快