查看原文
其他

MATLAB程序设计语言(3.4)---传值机制和COW

洞穴之外之编程哥 理念世界的影子 2021-06-23

公众号:理念世界的影子

文不可无观点,观点不可无论据。

转载请注明出处



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(结构体和元胞数组的底层实现)

用圆搞定FBB---FBB、Matlab与航天

MATLAB程序设计语言(3)---一切皆为数组2(MATLAB的底层实现)

MATLAB程序设计语言(3.1)---一切皆为数组1

MATLAB程序设计语言(2.1)---变量的作用域

MATLAB程序设计语言(2)---help的see also与六度空间理论

MATLAB程序设计语言(1)---入门


微信扫一扫

关注“理念世界的影子”

版权声明:本文是"洞穴之外"作者原创文章,欢迎转载,须署名并注明来自“理念世界的影子”公众号。


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存