提高将numpy数组转换为MATLAB的性能

5Ke*_*5Ke 7 python matlab numpy

从Python调用MATLAB必然会通过在Python中重写(很多)代码来减少性能.然而,这对我来说不是一个现实的选择,但令我很恼火的是,效率的巨大损失在于从numpy数组到MATLAB double的简单转换.

我在谈论从data1到data1m的以下转换,其中

data1 = np.random.uniform(low = 0.0, high = 30000.0, size = (1000000,))
data1m = matlab.double(list(data1))
Run Code Online (Sandbox Code Playgroud)

这里matlab.double来自Mathworks自己的MATLAB包/引擎.第二行代码在我的系统上花费了20秒,对于一个除了使数字"可食用"MATLAB之外没有真正做任何事情的转换似乎太多了.

所以基本上我正在寻找与此处给出的相反的技巧,该技巧适用于将MATLAB输出转换回Python.

max*_*111 7

有效地传递numpy数组

看一下文件mlarray_sequence.py夹中的文件PYTHONPATH\Lib\site-packages\matlab\_internal.在那里你会发现MATLAB数组对象的结构.性能问题来自于在generic_flattening函数内使用循环复制数据.

为了避免这种行为,我们将稍微编辑一下文件.此修复程序应适用于复杂和非复杂的数据类型.

  1. 如果出现问题,请备份原始文件.
  2. 添加import numpy as np到文件开头的其他导入
  3. 在第38行你应该找到:

    init_dims = _get_size(initializer)  # replace this with 
         try:
             init_dims=initializer.shape
         except:
             init_dims = _get_size(initializer)
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在第48行你应该找到:

    if is_complex:
        complex_array = flat(self, initializer,
                             init_dims, typecode)
        self._real = complex_array['real']
        self._imag = complex_array['imag']
    else:
        self._data = flat(self, initializer, init_dims, typecode)
    
    #Replace this with:
    
    if is_complex:
        try:
            self._real = array.array(typecode,np.ravel(initializer, order='F').real)
            self._imag = array.array(typecode,np.ravel(initializer, order='F').imag)
        except:
            complex_array = flat(self, initializer,init_dims, typecode)
            self._real = complex_array['real']
            self._imag = complex_array['imag']
    else:
        try:
            self._data = array.array(typecode,np.ravel(initializer, order='F'))
        except:
            self._data = flat(self, initializer, init_dims, typecode)
    
    Run Code Online (Sandbox Code Playgroud)

现在,您可以将numpy数组直接传递给MATLAB数组创建方法.

data1 = np.random.uniform(low = 0.0, high = 30000.0, size = (1000000,))
#faster
data1m = matlab.double(data1)
#or slower method
data1m = matlab.double(data1.tolist())

data2 = np.random.uniform(low = 0.0, high = 30000.0, size = (1000000,)).astype(np.complex128)
#faster
data1m = matlab.double(data2,is_complex=True)
#or slower method
data1m = matlab.double(data2.tolist(),is_complex=True)
Run Code Online (Sandbox Code Playgroud)

MATLAB阵列创建的性能提高了15倍,界面现在更容易使用.

  • 是的,但显然更有效;)。另请注意:如果您正在保存和加载一个相对较小的 mat 文件,matfile 会被您的工作系统缓存在内存中,因此没有磁盘 I/O 开销。如果文件变大,这看起来会有点不同。我无法查看编译后的界面代码,但如果 Matworks 做了与 python 界面类似的工作,我并不惊讶保存和加载效率更高。 (2认同)

5Ke*_*5Ke 4

在等待更好的建议的同时,我将发布迄今为止我想到的最佳技巧。归结为使用 `scipy.io.savemat\xc2\xb4 保存文件,然后在 MATLAB 中加载该文件。

\n\n

这不是最漂亮的黑客,它需要一些小心以确保依赖同一脚本的不同进程不会最终编写和加载彼此的 .mat 文件,但性能提升对我来说是值得的。

\n\n

作为测试用例,我编写了两个简单、几乎相同的 MATLAB 函数,需要 2 个 numpy 数组(我使用长度 1000000 进行测试)和一个 int 作为输入。

\n\n
function d = test(x, y, fs_signal)\nd = sum((x + y))./double(fs_signal);\n\nfunction d = test2(path)\nload(path)\nd = sum((x + y))./double(fs_signal);\n
Run Code Online (Sandbox Code Playgroud)\n\n

函数test需要转换,同时test2需要保存。

\n\n

测试test:在我的系统上转换两个 numpy 数组需要大约 40 秒。准备和运行测试的总时间降至170 秒

\n\n

测试test2:在我的系统上保存数组和 int 大约需要 0.35 秒。令人惊讶的是,在 MATLAB 中加载 .mat 文件非常高效(或者更令人惊讶的是,它在处理双精度数时效率极低)...准备和运行 test2 的总时间降至0.38 秒

\n\n

性能提升了近 450 倍......

\n