当前MindSpore对于Windows平台的支持仍有欠缺,比如不支持GPU,编译工具链导致的无法编译debug版本而影响问题定位效率等。今年MindSpore将尝试通过社区如易用性sig协作的方式,共同把这些问题解决,为MindSpore的Windows用户提供更好的使用体验。
个人以前也没有Windows以及MindSpore框架开发的经验,在网络搜索关于MindSpore的资料时,找到的更多是关于如何使用MindSpore进行网络实现和训练、推理,没有从框架贡献者的角度介绍如何参与或者给MindSpore贡献代码的内容。所以,想借这个机会记录好Windows下这些问题解决的过程,既能联合社区的朋友们解决MindSpore在Windows下的问题,也能提升自己对于Windows下开发、对于MindSpore在架构和代码层面的理解,还可以为想给MindSpore贡献代码的同学提供一些参考。
在开始解决实际的问题前,我想第一步应该是参考MindSpore官网的安装指南来完成Windows下CPU版本的编译,本文就是记录下我编译安装的过程以及发现的问题。
Windows下编译MindSpore CPU版本
1. 安装依赖软件
MindSpore的CPU版本在编译时依赖软件及安装方式如下表:
软件 | 版本要求 | 用途 | 安装方法 |
---|---|---|---|
Windows | 10,64位 | 操作系统平台 | |
python | 3.9.0/3.7.5 | Python语言解释器 | 3.9.0 下载地址,直接安装即可 |
mingw | 7.3.0 | 编译工具链 |
下载地址,解压后将目录加入到Path 环境变量下 |
git | 2.29.0 | 版本管理工具 | 下载地址,下载直接安装即可 |
cmake | >3.18.3 | 构建工具 | 下载地址,下载64位MSI版本直接安装即可 |
strawberry perl | 5.28.1.2801 | perl编译器 |
下载地址,解压后将Perl的bin目录加到Path 环境变量中 |
wheel | >0.32.0 | Python打包工具 | 执行pip install wheel 即可 |
官网的Windows编译安装指南中推荐使用ActivePerl,但其下载过程中还需要注册,体验并不好,所以我使用了strawberry perl,而且最好是zip包,因为MSI的安装会将额外的gcc编译工具链添加到
Path
中,影响编译中gcc编译器的选择;
对于以上需要安装的工具,exe
以及msi
格式的软件包都可以自动配置bin
目录到Path
环境变量中(有些需要在安装过程中指定,如cmake),而压缩包版本则需要在解压后,手动在Windows的环境变量Path
中添加新的路径。如下图,如果strawberry-perl-5.28.2.1-64bit.zip
被解压到D盘的根目录,可以在系统变量Path
或者用户变量Path
中增加D:\Strawberry\perl\bin
的项目,同理,也需要增加mingw bin的路径,如图中的D:\mingw64\bin
。
另一个需要配置的是pip的镜像源,可以考虑使用华为云Python镜像,在C:\Users\<UserName>\pip\pip.ini
中添加如下内容即可:
[global]
index-url = https://repo.huaweicloud.com/repository/pypi/simple
trusted-host = repo.huaweicloud.com
timeout = 120
2. 下载MindSpore源码编译
首先通过git clone --depth=1 https://gitee.com/mindspore/mindspore.git -b r1.6
下载框架的源码,这里指定了1.6版本的tag,原因当前的master分支编译会出错……。
然后启动Windows的CMD,然后切换到代码仓库的目录,正式开始编译前可以设置下patch
的环境变量MS_PATCH_PATH,以防止在三方库打patch时因为git安装路径的问题找不到patch
命令,方式和第一部分中添加Path
环境变量类似。首先在cmd中通过where git
找到git的安装目录,如D:\Program Files\Git\cmd\git.exe
,然后创建一个新的用户环境变量MS_PATCH_PATH
,赋值为D:\Program Files\Git\usr\bin
。最后在cmd 输入 refreshenv
完成环境变量刷新。
MindSpore代码仓根目录下的build.bat
是启动编译的批处理脚本,脚本中有两处变量会影响到编译的效率:
-
SET threads=8
:设置并行编译的线程数,默认为8。如果你的处理器支持的线程数更高,可以修改为更大的数字,比如我的CPU是8核16线程,这个地方我就改成了16; -
SET ENABLE_GITEE=ON
:是否通过gitee下载三方库的压缩包,默认从github下载。如果网络访问外网环境不太好,可以选择修改为ON
,加快下载速度;
这些准备完成后,就可以在cmd中执行call build.bat
开始编译,正常大约1小时左右就可以完成编译,生成mindspore的whl包。
3. 安装编译版本并检查
MindSpore的编译好的whl包路径在build\package\
下,切换到该目录下,使用pip install mindspore-1.6.1-cp39-cp39-win_amd64.whl
,执行运行检查:
D:\Workspace\mindspore\build\package>python -c "import mindspore;mindspore.run_check()"
MindSpore version: 1.6.1
The result of multiplication calculation is correct, MindSpore has been installed successfully!
可以运行,表明编译成功。
发现的问题/改进点
如果一切顺利,整个的编译时间大概在一小时多一点,但实际上我花了大约1天的时间,原因是在过程中碰到了一些列的问题。
- 使用conda的Python环境,编译Protobuf失败。如果你的Python环境是通过conda去做管理的,在编译Protobuf三方库会遇到这样的错误:
-- 3.13.0.0
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Found ZLIB: C:/Users/lenovo/.conda/envs/py39/Library/lib/z.lib (found version "1.2.11")
-- Performing Test protobuf_HAVE_BUILTIN_ATOMICS
-- Performing Test protobuf_HAVE_BUILTIN_ATOMICS - Success
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Workspace/mindspore/build/mindspore/_deps/protobuf-src/_build
Scanning dependencies of target libprotobuf-lite
In file included from D:\Workspace\mindspore\build\mindspore\_deps\protobuf-src\src\google\protobuf\arena.cc:37:0:
C:/Users/lenovo/.conda/envs/py39/Library/include/google/protobuf/stubs/mutex.h: In constructor 'constexpr google::protobuf::internal::WrappedMutex::WrappedMutex()':
C:/Users/lenovo/.conda/envs/py39/Library/include/google/protobuf/stubs/mutex.h:124:29: error: temporary of non-literal type 'google::protobuf::internal::CallOnceInitializedMutex<std::mutex>' in a constant expression
constexpr WrappedMutex() {}
^
……
在protobuf.cmake
中用-Dprotobuf_WITH_ZLIB=OFF
屏蔽zlib后可以完成编译,但具体影响的功能不祥。
mindspore_add_pkg(protobuf
VER 3.13.0
LIBS protobuf
EXE protoc
URL ${REQ_URL}
MD5 ${MD5}
CMAKE_PATH cmake/
CMAKE_OPTION -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_BUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -Dprotobuf_WITH_ZLIB=OFF
PATCHES ${PROTOBUF_PATCH_ROOT}/CVE-2021-22570.patch)
通过上面的日志发现编译时用到了conda的zlib、protobuf头文件,并非框架下载zlib和protobuf,推测根源的问题在这里。检查系统环境变量,发现miniconda在安装时自动的在Path环境变量中添加了mingw的类库路径,而这个路径下包含了编译框架需要的zlib、protobuf的一些头文件,版本不一致导致了protobuf的编译错误。将下图中包含Library
路径从Path变量中去掉后,在不修改protobuf.cmake
的情况下也可以正常编译。
- master分支编译完成安装后出现导入错误。具体的错误信息如下:
(py39) D:\Workspace\mindspore\build\package>python -c "import mindspore;mindspore.run_check()"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "D:\Workspace\mindspore\build\package\mindspore\__init__.py", line 29, in <module>
from .rewrite import *
ModuleNotFoundError: No module named 'mindspore.rewrite'
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "D:\Workspace\mindspore\build\package\mindspore\__init__.py", line 29, in <module>
from .rewrite import *
ModuleNotFoundError: No module named 'mindspore.rewrite'
屏蔽对应的代码行可以正常运行,但这个错误不应该出现,master上合入的代码应该保证基本的功能,这里的自动化测试环节没有能照顾到这个基础的健康检查。这也是为什么上面的编译使用了1.6版本的分支而非master的原因。
- 是否有必要引入
perl
这个依赖。我检查了MindSpore代码仓中对于perl
的使用,结果发现Windows下只有protobuf
的cmake有使用,
if(WIN32)
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/proto/${file_name}.pb.cc"
"${CMAKE_BINARY_DIR}/proto/${file_name}.pb.h"
"${CMAKE_BINARY_DIR}/proto/${file_name}_pb2.py"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
...
COMMAND perl -pi.bak -e "s/import (.+_pb2.*)/from . import \\1/"
"${CMAKE_BINARY_DIR}/proto/${file_name}_pb2.py"
...
COMMENT "Running C++ protocol buffer compiler on ${file}" VERBATIM)
这里perl命令的作用是把所有*_pb2.py
的python文件中的import xxx as xxx
替换为 from . import xxx as xxx
。如果没有其它的库依赖perl,这里可以考虑用inline的python命令、cmd命令行工具或者sed(for windows)来替换,最优的情况下可以在安装和编译的过程中减少一个依赖,提升使用体验。
总结
本次的MindSpore CPU版本编译过程中发现了3个问题:conda管理的python环境编译、master编译后的执行错误以及编译依赖perl
的问题,针对这几个问题,我在gitee的代码仓中创建了issue并添加kind/bug
和comp/build-install
标签,方便负责相应领域的开发人员进行分析和解决问题。
编译的过程中出现了大量的三方库以及模块的编译过程,要理解MindSpore的编译,首先得理清楚过程中都依赖了什么,编译了那些模块,功能和作用是什么,这也是下一步需要分析和完成的内容。