本文发自 http://www.binss.me/blog/run-azurlaneautoscript-on-arm64/,转载请注明出处。

最近在同事的安利下入坑碧蓝航线,发现这个游戏太 tm 肝了,每天重复劳动超过一小时,为此 github 上搜寻了一番挂机脚本,发现了 https://github.com/LmeSzinc/AzurLaneAutoScript (后简称 ALAS) ,根据 文档 ,我选择了在 x86 windows PC 上运行夜神模拟器 + 脚本的方式,试用完后一阵狂喜,彻底解放了生产力。

但我的 PC 由于功耗过高,一般只有在打游戏的时候开启,其他时间处于睡眠状态。而挂机需要 24h 不间断,因此 PC 不是合适的选择。左右环顾,手头上常年开机的只有一台群晖 DS918(x86) 和一台日常主力机 Mac Studio (arm64)。于是我开始了我的折腾。

ARM64 运行模拟器

首先在群晖上运行安卓模拟器毫无疑问是不可行的,因为 j3455 带不动。所以我选择在 Mac Studio 上跑模拟器。不幸的是,在 Apple Silicon 下,几乎所有的安卓模拟器都不支持,包括当年经常用的老朋友 genymotion 。幸运的是,官方的 Andriod Studio 内带的 device emulator 能支持。安装之:

brew install --cask android-studio

装完后启动应用,在首页 - More Actions - Virtual Device Manager ,可以创建设备

我用了如下配置供参考,注意安卓版本不要太高,太高会导致启动碧蓝航线的时候黑屏,不知道为啥。

创建完设备后启动,安装游戏即可:

x86 运行 ALAS

由于 AzurLaneAutoScript 项目没有 ARM64 的 build ,为了省事考虑还是考虑构建 x86 的版本。AzurLaneAutoScript 项目提供了 Dockerfile ,于是我直接根据 https://github.com/LmeSzinc/AzurLaneAutoScript/wiki/Installation_en_docker 进行构建。

不幸的是,服务挂了。于是我加入了 AzurLaneAutoScript 开发者群,在群里大佬的指导下,发现是跑 mxnet core 了,报了 Illegal instruction 。作为一个 C++ API Boy 对这个很熟悉了,一般是运行了 SIMD 指令但机器不支持。对于 mxnet 这种机器学习库显然不会错过用 SIMD 来加速。而对于 Mac Studio ,根据 https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment 显然不支持:

Rosetta translates all x86_64 instructions, but it doesn’t support the execution of some newer instruction sets and processor features, such as AVX, AVX2, and AVX512 vector instructions. If you include these newer instructions in your code, execute them only after verifying that they are available. For example, to determine if AVX512 vector instructions are available, use the sysctlbyname function to check the hw.optional.avx512f attribute.

一番搜索,解决办法很简单,项目依赖的是 mxnet==1.6.0 ,在所有依赖装完后把 mxnet 卸载掉,安装最新版即可,目测是 USE_SSE 选项在 1.6.0 里被打开了。

image 构建完成后,启动容器:

docker run -it -e TZ=Asia/Shanghai --volume=/Users/binss/Desktop/AzurLaneAutoScript:/app/AzurLaneAutoScript:rw -p 22267:22267 --name azurlaneautoscript azurlaneautoscript

ALAS 连接模拟器

容器中的 AzurLaneAutoScript 需要连接 host 上的安卓模拟器,host 可以通过 host.docker.internal 访问,而端口需要在 host 上看看:

$ lsof -iTCP -sTCP:LISTEN -n -P | grep qemu
qemu-syst 43049 binss   42u  IPv4 0xbccf4cd072e9da6f      0t0  TCP 127.0.0.1:5555 (LISTEN)
qemu-syst 43049 binss   43u  IPv6 0xbccf4cd06cecd9f7      0t0  TCP [::1]:5555 (LISTEN)
qemu-syst 43049 binss   44u  IPv4 0xbccf4cd072c96f8f      0t0  TCP 127.0.0.1:63524 (LISTEN)
qemu-syst 43049 binss   45u  IPv6 0xbccf4cd06cecfc57      0t0  TCP [::1]:63524 (LISTEN)
qemu-syst 43049 binss   46u  IPv4 0xbccf4cd071adda6f      0t0  TCP 127.0.0.1:5554 (LISTEN)
qemu-syst 43049 binss   47u  IPv6 0xbccf4cd06ced60d7      0t0  TCP [::1]:5554 (LISTEN)
qemu-syst 43049 binss   54u  IPv6 0xbccf4cd06ced6e97      0t0  TCP 127.0.0.1:8554 (LISTEN)
qemu-syst 43049 binss   65u  IPv4 0xbccf4cd072e3c4af      0t0  TCP 127.0.0.1:65358 (LISTEN)

根据官方文档,5555 即为 adb 端口,因此在 ALAS 的 模拟器 Serial 填入 host.docker.internal:5555

arm64 运行 ALAS

然而,在 Mac Studio 上以模拟 x86 的方式运行 ALAS 性能并不理想。最典型的就是自动委托界面文字的 OCR,识别完成需要十几秒。另一方面,运行过程 CPU 占用较高,高峰时达到了 200%,而 native 的安卓模拟器只占不到 90% 。

那么能否让 ALAS 在 arm64 下 native 运行呢?为了实现这个想法,我整整折腾了一天。

主要拦路虎还是 mxnet ,即使是最新版,对 arm64 的支持也是很差的。根据 https://github.com/apache/incubator-mxnet/issues/19234 ,需要去安装一个 ArmPL ,但它是 OS Specify 的,我尝试装了几个版本都没能跑起来。无奈之下,考虑自己编译。

arm64 编译 mxnet

编译 mxnet 是一件浪费时间且令人心累的过程,建议等官方支持,或直接安装我编译好的 wheel

  1. 安装依赖:apt-get install -y build-essential git ninja-build ccache libopenblas-dev libopencv-dev cmake
  2. clone 代码:git clone --recursive https://github.com/apache/incubator-mxnet mxnet ,checkout 到 1.9.1 branch
  3. make

编译过程需要大量的内存,一开始给 docker 分配了 8g 结果 OOM 了,后来调到 20G,四核编译了半小时)呵呵。

编译完后,通过 pip 安装 mxnet 目录下的 python 目录,当然在此之前,记得把已安装的 mxnet 1.6.0 卸载掉:

pip uninstall mxnet
pip install --user -e ./python

其他依赖

针对 arm64,我重新写了个 Dockerfile :

# First arm64 version
FROM continuumio/anaconda3:2021.11

# Set remote and local dirs
WORKDIR /app

# Install the base conda environment
# Cannot find python 3.7.6 for arm64, so use 3.7.10
ENV PYROOT=/app/pyroot
RUN conda create --prefix $PYROOT python==3.7.10 -y

# CV2 requires libGL.so.1
RUN apt-get update && apt-get install -y libgl1 adb libatlas-base-dev libopencv-dev build-essential && rm -rf /var/lib/apt/lists/*

# Install the requriements to the conda environment
COPY ./requirements.txt /app/requirements.txt
RUN $PYROOT/bin/pip install -r /app/requirements.txt

COPY ./mxnet-1.9.1-py3-none-any.whl /app/mxnet-1.9.1-py3-none-any.whl
RUN $PYROOT/bin/pip uninstall mxnet -y && $PYROOT/bin/pip install /app/mxnet-1.9.1-py3-none-any.whl

ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/app/pyroot/mxnet/"

# When running the image, mount the ALAS folder into the container
CMD $PYROOT/bin/python /app/AzurLaneAutoScript/gui.py

其依赖于 requirements.txt :

adbutils==0.11.0
scipy
pillow
opencv-python
imageio
lz4
tqdm
uiautomator2==2.16.7
retrying
cnocr==1.2.2
jellyfish
pyyaml
inflection
pywebio==1.5.2
starlette==0.14.2
anyio==1.3.1
uvicorn[standard]==0.17.6
aiofiles
wrapt==1.13.1
prettytable==2.2.1
pypresence==4.2.1
alas-webapp==0.3.7
rich==11.0.0
zerorpc
atomicwrites

mxnet-1.9.1-py3-none-any.whl

或直接用我的 image :binss/azurlaneautoscript:arm64

换用 arm64 版本后,ALAS 性能提升明显,OCR 耗时显著降低,CPU 占用降低到 50% 左右。