1. 引言
MATLAB是一款功能强大的数学计算软件,它在图像处理、数据分析、控制系统、深度学习和信号处理等领域应用颇广 [1]。MATLAB的基本数据单位是矩阵,这一特性使得它在解决许多数学和工程问题时要比其他语言更为简捷 [2],因此,本公司在开发自动心电分析软件包时,选择了MATLAB作为研发心电分析算法的工具,并编写了autoECG软件包,用美国麻省理工大学心电数据库的不同数据对该软件进行检验,部分输出结果如图1所示(共输出215条诊断结果及诊断时间)。
Figure 1. Some results of running autoECG software package under MATLAB
图1. MATLAB下autoECG软件包运行部分结果
由于用MATLAB编写的代码不能脱离MATLAB环境独立运行,无法直接用于产品软件的发布,同时,考虑到本项目中其它软件均采用C语言进行开发,所以需要用C程序调用MATLAB,由此产生了混合编程的需求 [3] [4]。
MATLAB与C混合编程方法主要有以下几种:MATLAB代码转C源代码 [5]、MATLAB代码转C动态链接库 [1] [2] [3] [4]、MATLAB代码部署到MATLAB Production Server (以下简称MPS)并用C客户端远程调用、C程序调用MATLAB引擎 [6]。其中,C程序调用MATLAB引擎方法由于其用户环境必须安装有MATLAB,并不适合软件的发布,因而在前期调研时便舍弃了该方法。本文基于autoECG软件包对其余三种混合编程方法进行了实践,分析比较了它们各自的优缺点,讨论了它们在不同应用场景下的适用性 [7]。
本文涉及到的软件如下:NetBeans IDE 12.0,MATLAB R2020a,GCC 8.3.1,OpenSSL 1.1.1c。运行环境为:CentOS 8.1.1911 (Core版本,x86_64架构),Ubuntu 18.04 (x86_64架构)。
2. MATLAB转C源代码方法
利用MATLAB Coder工具将MATLAB代码自动转换为C/C++源代码。此法经过笔者的实践证明实用性较差,原因是有些MATLAB系统函数不能直接转换成C源代码,如图2所示。
Figure 2. Matlab coder cannot convert some functions
图2. MATLAB Coder无法转换部分函数
在实际应用时,MATLAB代码中常常会包含大量的MATLAB系统函数,难以避免地会使用到无法自动转换成C源代码的MATLAB函数。由于该局限性,笔者在实际应用没有采用此法。
3. MATLAB转C动态链接库方法
3.1. 方法流程
利用MATLAB自带的Library Compiler工具将MATLAB代码编译成C动态链接库,然后在C代码中调用生成的动态链接库。该方法生成的动态链接库依赖于MCR (MATLAB Compiler Runtime,简称MATLAB Runtime),因此用户环境必须安装MCR。MATLAB转C动态链接库方法流程如图3所示:首先准备好我们的MATLAB函数(图中以ECGmainfcn.为例),并配置MATLAB Library Compiler将GCC设置为C编译器,然后用MATLAB Library Compiler将MATLAB函数编译成C动态链接库和头文件。用户在安装了对应版本的MATLAB Runtime的环境下即可在程序中调用上面得到的动态链接库,进行混合编程 [8]。
Figure 3. Method and flow of MATLAB to C dynamic link library
图3. MATLAB转C动态链接库方法流程
3.2. 实现过程
在CentOS8系统上,此法实现过程可分为以下三步:
1) 将MATLAB函数编译成动态链接库
首先需要指定mcc使用的编译器。在MATLAB命令行使用mbuild-setup命令来选择gcc作为C编译器。然后通过如下mcc命令将autoECG软件包编译为动态链接库,库名为libautoECG.so,输出文件存放在同级目录project下:
mcc -W lib:libautoECG -T link:libautoECG.m -d ./project/
2) 配置用户环境
首先配置MCR环境。下载MCR安装包并安装,然后将MCR安装目录下的externs目录内全部头文件拷贝到/usr/local/include目录下。接着根据1)中输出目录project下的readme文件提示设置环境变量XAPPLRESDIR和LD_LIBRARY_PATH。注意:笔者的XAPPLRESDIR设置与readme文档所述不同:
XAPPLRESDIR=/usr/share/X11/app-defaults
并且笔者额外设置了一个环境变量LIBRARY_PATH,以保证C代码能够编译成功:
LIBRARY_PATH=${LD_LIBRARY_PATH}
接下来修改/etc/ld.so.conf文件,在末尾加一行:/usr/local/lib,并将输出目录project下的autoECG.so文件拷贝到/usr/local/lib目录下。最后执行ldconfig命令,环境配置完成。
3) 在C代码中调用动态链接库
代码流程为:初始化MATLAB运行环境和函数、准备MATLAB函数入参、调用MATLAB函数、处理返回数据、终止MATLAB运行环境、释放资源、退出程序。
笔者采用NetBeans IDE作为开发环境,通过ssh连接至Linux主机来进行远程开发。在开始编码前需要将输出目录project/中的头文件autoECG.h拷贝至项目头文件目录中,以在代码中对其进行引用。
在编译代码前需要添加链接库选项:
-lautoECG -lmwmclmcrrt
图4是C程序在Linux系统下调用autoECG软件包运行的结果(省略部分诊断结果输出),程序输出和返回值与图1完全一致。为了便于比较不同方法下的运行性能,程序末尾还打印了初始化MATLAB运行环境、MATLAB函数计算、终止MATLAB运行环境的耗时。
3.3. 优缺点分析
此法的缺点在于用户环境依赖MCR,安装MCR会占用一定大小的磁盘空间,间接增加了程序包的大小。同时,由图4运行结果可知,程序初始化过程耗时4秒左右,所需的时间成本较大。尽管初始化耗时较长,但在实际应用中可以通过控制程序初始化的次数来降低其时间成本。
此法的优点在于以简单有效的方式整合了MATLAB和C两种语言,充分发挥了两者的优势,应用空间十分广阔。而且将MATLAB函数打包编译成C动态链接库,使得修改算法实现只需替换动态链接库而无需修改C程序代码,程序开发非常灵活 [9]。
4. MPS结合C客户端方法
MPS是MATLAB发布的一款适用于企业生产环境的服务器。任何自定义的MATLAB函数都可以部署于MPS之上,用户可以通过调用多种语言的接口(以http/https请求的形式),或通过调用RESTful API,来访问部署于MPS上的MATLAB函数。下面描述配置部署MPS以及通过C客户端来调用MPS上的函数的方法、实现及关键技术。
4.1. 服务器环境搭建
服务器上需要安装MPS和MCR。注意,MCR的版本一定要与MATLAB的版本保持一致,且服务器搭载的Linux系统必须是符合unix系统标准的发行版,如笔者使用的CentOS8。安装好MPS之后,添加${MPS安装目录}/script路径到环境变量PATH的末尾。
4.2. 管理和配置MPS实例
MPS实例进程是以守护进程的形式运行于Linux系统上的服务器进程,它依赖于MCR和MPS。一个MPS实例可以看成一台服务器,它能够与多个客户端相连,并行处理客户端的分析请求。一个Linux系统上可以同时存在多个MPS实例,它们在逻辑上相互独立,且可以使用不同版本的MCR。每个MPS实例都会占用独立的端口,用于和客户端进行通讯。图5为MPS的部署架构示例图。
Figure 5. MPs deployment architecture example
图5. MPS部署架构示例
在创建MPS实例之前,我们需要通过mps-setup命令指定它所使用的MCR的路径,当系统上存在多个MCR或MCR没有按照默认路径安装时,这一步尤为关键。接下来,使用mps-new命令创建一个MPS实例。
由于MPS默认使用http协议,而这是一种不安全的应用层协议,如非特殊需求我们都应该关闭MPS实例的http端口,并启用https端口来与客户端进行更为安全的连接。具体方法是:通过修改MPS实例的配置文件(路径为${MPS实例目录路径}/config/main_config)来启用https端口:
--https 9920#启用https端口
#启用客户端证书验证
--ssl-verify-peer-mode verify-peer-require-peer-cert
#服务器私钥路径
--x509-private-key ./x509/server.key
#服务器私钥密码文件路径
--x509-passphrase ./x509/pwd
#服务器证书链文件路径
--x509-cert-chain ./x509/cert_chain.pem
#服务器根CA证书路径
--x509-ca-file-store /etc/pki/CA/cacert.pem
服务器采用自签名的CA证书即可。使用openssl生成CA私钥、CA证书、服务器私钥和客户端私钥,再用CA证书和私钥生成服务器证书和客户端证书。
配置完成之后,使用mps-start命令启动MPS实例,并用mps-status命令确认MPS实例是否成功启动。
4.3. 客户端环境配置
客户端程序运行于Ubuntu (非unix标准)操作系统上。首先将服务器上${MPS安装目录路径}/client/c目录(以下简称c目录)拷贝至客户端操作系统上。接着添加${c目录路径}/glnxa64/lib路径到环境变量LD_LIBRARY_PATH和LIBRARY_PATH的末尾。
4.4. 部署MATLAB函数并调用
下面把autoECG转换成可部署文件并部署到MPS实例,再用C客户端调用MPS实例上部署的MATLAB函数。
首先在MATLAB命令行通过如下命令,将autoECG软件包转换为可部署文件(.ctf后缀),输出文件存放在同级目录project中:
mcc-W CTF:autoECG -U autoECG.m -d ./project/
然后将输出目录project/中的autoECG.ctf文件拷贝至MPS实例目录下的auto_deploy目录中,MPS实例会自动检测该目录下的.ctf文件并将其部署。检查日志文件${MPS实例目录路径}/log/main.log来确认MATLAB函数已成功部署。
autoECG.ctf部署成功后,编写客户端代码来连接MPS实例并调用该函数。将${c目录路径}/include/mps/目录下的头文件拷贝至项目头文件目录。再将4.2节中生成的客户端证书和私钥以及CA证书拷贝至项目目录下,并将它们的路径设置到mpsClientConfig结构体中,如图6为笔者编写的设置函数。
接下来的代码流程与3.2节中步骤3的流程类似:准备MATLAB函数入参、连接MPS调用MATLAB函数、处理返回数据、释放资源、退出程序。笔者编写的代码实现了连续对MPS实例请求autoECG算法,并将每一次请求耗时和得到的返回值result打印输出。程序执行结果如图7所示,返回值result与图1相同,程序调用autoECG成功。
Figure 7. Client program execution results
图7. 客户端程序执行结果
4.5. 优缺点分析
此法的缺点在于函数调用以网络请求的方式实现,对于单机软件或有网络条件限制的场景不适用。同时,由图7运行结果可看出,在MPS实例启动后的第一次请求耗时为正常情况下请求耗时的两倍多,这一现象会导致服务器初始化的时间成本增加。
此法的优点在于将MATLAB函数以服务的形式单独部署于服务器上,将算法实现与用户程序完全解耦;同时MPS的设计已经较为完善,它具有自动化部署、同时支持多个MCR版本、支持数据缓存、支持多处理器、支持低延迟并发请求、提供多种语言客户端及RESTful API等等强大的功能,且具有良好的可扩展性和安全机制,适用于企业级系统。采用此法可以减少大量开发人员的工作,避免“重复造轮子”。
5. MPS结合C客户端方法与MATLAB转C动态链接库方法的比较
下面从空间成本、时间成本和限制条件三个方面对两种方法进行比较,以分析它们各自的适用场景。为表示方便,以下用方法一指代MPS结合C客户端方法,方法二指代MATLAB转C动态链接库方法。
5.1. 空间成本
对于方法一,需要从服务器端和客户端两方面来分析:服务器端需要安装MCR、MPS,客户端则无需安装任何MATLAB环境。方法二要求用户环境安装有MCR,这会占用较大的磁盘空间(MCR大小为几百Mb到几Gb不等,取决于MCR版本等因素)。所以,针对用户程序来说,方法一的空间成本要比方法二小很多。
5.2. 时间成本
对于方法一,同样从服务器端和客户端两方面来分析:服务器端创建、配置、启动MPS实例以及初始化MATLAB函数需要一定的时间,客户端请求调用函数则无额外的时间成本。方法二中用户程序需要耗费一定时间来初始化和终止MATLAB运行环境和函数。同时,从计算速度的角度来说,方法一由服务器来进行计算,因此计算速度很大程度上取决于服务器配置和网络环境,而方法二则由用户程序来进行计算,计算速度主要取决于用户环境。对于用户计算机的计算能力较差的情况,方法一可以提供比方法二更稳定快速的算法调用。所以,方法一的时间成本比方法二要小很多。
5.3. 限制条件
方法一的限制条件是服务器与客户端之间必须网络畅通,如发生服务器宕机、客户端不能联网等情况,程序就无法调用算法。方法二的限制条件是用户环境依赖于MCR,如果用户的计算机难以满足MCR的运行条件,程序就无法正常工作。
5.4. 适用场景
由以上分析可知,在大部分应用场景下,尤其是在企业级系统、科研系统等大型系统中,方法一要比方法二更适用;而在开发单机应用程序或网络条件受限等场景下,方法二比方法一更适用,如表1所示:
Table 1. Comparison of two hybrid programming methods
表1. 两种混合编程方法的比较
6. 总结
MATLAB作为世界上最强大的科学计算软件之一,广泛应用于科学研究和工程设计等领域 [2]。本文主要研究了两种通过C程序调用MATLAB算法的方法,其中MATLAB转C语言动态链接库方法更适用于单机应用程序的开发,MPS结合C客户端方法则更适用于大小型系统及应用程序的开发。两种方法都已被应用于本公司承担的“湖南创新型省份建设专项重点领域研发计划动态多参融合家庭智能诊断高端设备关键技术研究及产业化”项目的研发中。实践表明,通过将MATLAB与C语言有机结合,能够充分发挥两者的优势,大大扩展MATLAB的应用空间,提高程序设计和开发效率,具有很高的应用价值。
基金项目
“湖南创新型省份建设专项重点领域研发计划——动态多参融合家庭智能诊断高端设备关键技术研究及产业化”项目。