在使用matlab编程的时候,通常情况下,用户无需关心数据类型,matlab默认就是double类型,但某些特殊情况下,需要考虑数据类型。
1、进行大型矩阵存储的时候,为了节约内容,要用具体的数据类型,比如图像数据,要用uint8
2、混合编程的时候,采用.net/java等语言调用matlab sdk的时候,从.net/java传入的参数类型
笔者使用matlab进行图像处理,同时该图像处理函数用MATLAB Production Sever(mps)建立http接口被java调用,产生了一个奇怪的bug。在java调用前,该函数在matlab环境中经过测试,没有问题。
代码大概是这样
function flag = raw2tif(filename,xsize,ysize,zsize)
try
rawdata = rawread_t(rawfile,0);
rawdata = rawdata(1:xsize*ysize*zsize);
rawdata = reshape(rawdata,xsize,ysize,zsize);
rawdata = permute(rawdata,[2 1 3]);
% data = bigimage(data);
% write(data,tiffile);
option.big = true;
option.overwrite = true;
flag = saveastiff(rawdata,tiffile,option);
catch ME
warning(['rawsize is %d,xize is %d,ysize is %d,zsize is %d,...' ...
'rawsize-xszie*ysize*zsize is %d'],numel(rawdata),xsize,ysize,zsize,numel(rawdata)-pixelnum)
rethrow(ME)
end
这个bug提示
request size exceed 2147483647,exceed the maxinum of the limit.
经过查询,笔者发现这个是mps服务器的一个请求数据的最大限制
Set the maximum size of a request – MATLAB (mathworks.com)
但是,笔者传入函数的参数,仅仅是一个字符串和几个数值,数据根本没多少,而笔者在函数内部读取了一个图像文件,该文件大小2G,读入了一个22e8的数组里,该数组是一个uint8类型。如果说超出,那只能说是因为22e8>2147483647
因此,笔者怀疑,虽然调用函数的接口数据量很小,但mps毕竟是通过网络端口调用的,可能即便在函数内部也无法传输超过2147483647byte的数据。
至此,笔者觉得应该是mps服务器request size的问题,因此,笔者把接口从mps,切换到了java sdk,采用java直接调用,神奇的是,bug依然存在!!但错误不同了
请求的2147483647*1 (16.9GB)数组超过预设的最大数组大小(15.8GB)。这可能导致matlab无响应。
同时,catch到的warning的信息为
rawsize is 2236607142,xsize is 48034,ysize is 46563.
面对这个bug,笔者陷入了困境,因为笔者的电脑内存是128GB,而笔者创建的数组类型是uint8,因此,创建一个几万*几万的数组是毫无压力的。笔者也在matlab内部测试,建立一个100亿的数组都没什么问题,怎么建立一个22亿的数组会出超过预设最大数值大小的问题呢?此外,根据warning信息,显然,matlab内部已经是成功创建了这个2236607142*1的数组的,2236607142*1的数组都创建成功了,怎么还会报出无法创建2147483647*1的数组呢?
百思不得其解之时,笔者又编译了一个python包,python调用,没问题,执行成功!
这就很奇怪了,考虑到2147483647这个数字的特殊性,这是int类型的最大值。综合分析下,我考虑问题出在了
rawdata = rawdata(1:xsize*ysize*zsize);
问题可能出在了xsize*ysize*zsize上,java里面,传给xsize,ysize,zsize的类型是int,传到matlab里还是int,而xisze*ysize*zsize = 2236607142,超过了int的最大值。
至此,问题找到了,但matlab本身的debug机制有点问题,它对错误的定义非常不准确,导致笔者花费了大量的时间去研究这个问题。
笔者解决这个bug的第一个思路是,把传入的xszie,ysize,zsize转为uint64
xsize = uint64(xsize);
ysize = uint64(ysize);
zsize = uint64(zsize);
问题解决了,没有bug了,但笔者在执行代码的时候,内存飙升
读写1个2G的文件,居然用了40G的内存!!这也算一个新的bug问题了!
可以单独测试这个bug
a = randi(255,22e8,1,'uint8');
b = a(1:uint64(22e8));
执行之后,可以看到,matlab内存会飙升到40G
奇怪吧?
但是,如果把uint64去掉,直接写
a = randi(255,22e8,1,'uint8');
b = a(1:22e8);
执行这个代码,内存正常!
个人觉得,这也许是matlab内置机制的一个bug
最后笔者将xsize,ysize,zsize都转换为了double类型
xsize = double(xsize);
ysize = double(ysize);
zsize = double(zsize);
至此,问题得到了圆满的解决。
经此一役,笔者得到了几点认识和经验:
1、采用matlab编程时,尽可能使用double类型,少碰其他类型数据
2、涉及到混合编程,函数的参数要统一转换为double类型,可与防止意外的错误
3、matlab sdk的原理,包括mps,其实都是通过java/.net/python调用ctf文件,ctf再被matlab runtime执行,因为,打包sdk的时候,都有这个ctf文件
4、给大家提供一个混合编程bug的调试方法,那就是通过mps的测试服务器进行调试
这里又breakpoints可与选择,调试的时候,记得够上Enable CORS。