MATLAB程序设计语言(3.4)---传值机制和COW
公众号:理念世界的影子
文不可无观点,观点不可无论据。
转载请注明出处
MATLAB功能强大,编程方便,是国际广泛使用的计算软件。目前已有很多书籍介绍其在工程上的应用,但很少有从程序设计语言的角度写的书或文章。
申 明
本MATLAB系列均为笔者在闲暇时思考所得,偶有心得,欣喜之余,也迫切希望能与各位网友互相讨论,共同促进、共同提高。但鉴于MATLAB系列阅读量不高,互动不足,写完本篇后,近期不再更新。
MATLAB使用方便是众所周知的,那是否意味着其效率就低呢?MATLAB在设计时,采用了很多办法来解决效率问题。
写时复制机制(COW)
+
1 a=zeros(3,4);
2 fprintf('变量a地址 : ');dispaddr(a); % 显示a变量地址
3 dispmem(getaddr(a), 56); % 显示a变量头
4 b=a;
5 fprintf('变量b地址 : ');dispaddr(b); % 显示b变量地址
6 dispmem(getaddr(b), 56); % 显示b变量头
7 fprintf('b=a后变量a地址 : ');dispaddr(a); % 再次显示a变量地址
8 dispmem(getaddr(a), 56); % 显示a变量头
9 b(2)=3;
10 fprintf('修改b后变量b地址 : ');dispaddr(b); % 显示b变量地址
11 dispmem(getaddr(b), 56); % 显示b变量头
12 fprintf('修改b后变量a地址 : ');dispaddr(a); % 再次显示a变量地址
13 dispmem(getaddr(a), 56); % 显示a变量头
程序在第1、4、9行分别赋值a, b=a, 修改b,然后打印变量地址和mxArray头。执行后输出如下:
申请b=a后,发现了3个现象:
第13~16字节出现了以前未见过的信息,如果仔细观察,发现其就是变量a地址;
变量a的mxArray头变化了。赋值居然对原变量有影响!仔细观察,实际上是将b的地址注册进了a;
b变量的数据区地址与a一模一样。
修改b后,发现:
变量b的a引用被解除,而且被修改变量的数据区地址发生了变化;
变量a的b引用被解除,数据区仍保存。
同理,如果直接更改a,会发现a、b的引用被解除,同时b的数据区保存,a的数据区被更改。
上述现象就是MATLAB的写时复制机制(Copy on Write, COW)。通过这种方式,函数调用时传值几乎可以达到传址的效果,在提高效率同时,规避了传址的一些副作用(改变了原变量)。值得一提的是,COW的使用可能不止这里写的这么简单,很多的底层函数实现需考虑COW的影响,做起来不一定是那么容易的事情。
好在,这一切对于MATLAB用户而言都是黑盒子,我们完全可以用简单的语法形式,享受其效率。
传值机制
+
1 function test_passvalue
2 a=3;
3 passvalue(a) % 调用函数
4 a
5 function passvalue(a) % 函数中传入变量
6 a=4
在函数调用时,如果传入参数,并在程序中更改了此参数,退出后发现被传的参数不会改变。因此,可以认为MATLAB是传值的。
有时可能会担心,如果被调函数的参数是一个特别大的(譬如数百M的矩阵)数据,在函数调用时,对值的复制将产生巨大开销。
针对此,Fortran程序采用传址机制提高了效率,C语言有传递指针的传址机制,但MATLAB中并没有这种机制(除了handle类)。它采用了一种综合的方法。
explore_passvalue.m
1 function explore_passvalue
2 a=3;
3 fprintf('变量地址 : ');dispaddr(a); % 显示变量地址
4 dispmem(getaddr(a), 56); % 显示变量头
5 passvalue(a) % 调用函数
6 function passvalue(a) % 函数中传入变量
7 fprintf('变量地址传入函数后地址 : ');dispaddr(a);
8 dispmem(getaddr(a), 56);
运行后输出
运行后查看输出发现,MATLAB传入函数参数后,复制并改变的仅是参数的mxArray头,数据区并未改变。即使传入数百M的矩阵,MATLAB改变的仅仅是数字节的信息。
这种将传值机制与写时复制机制结合的策略,一方面大幅提高了程序运行效率,另外一方面,对传入值的改变不会更改传入数据本身,从而大幅提高了程序的透明性和安全性。
作为一件趣事,笔者发现,在编辑器中运行脚本a=[1,2];dispmem(getaddr(a),56);和在命令解释器中运行上述两个命令输出的结果是不同的。直接运行脚本,将发现变量a产生了一个影子变量。也许脚本的环境与基工作空间(base workspace)的环境并不是一个东西,而是它的一个影子吧。
往期文章:
MATLAB《MATLAB程序设计语言(3.3)---一切皆为数组3(结构体和元胞数组的底层实现)》
《MATLAB程序设计语言(3)---一切皆为数组2(MATLAB的底层实现)》
《MATLAB程序设计语言(2)---help的see also与六度空间理论》
微信扫一扫
关注“理念世界的影子”
版权声明:本文是"洞穴之外"作者原创文章,欢迎转载,须署名并注明来自“理念世界的影子”公众号。