Mac mini M1使用简单体验(编程、游戏、深度学习)
好久不见了各位!
前一阵子忍不住剁手买了M1芯片的mac mini,为了弥补自己的内疚感就卖了自己的旧的mbp2017款。数据也完全迁移到了新机器上,之前的工作也就由mbp2017彻底换成mac mini了,要换就换彻底点,不要给自己了留后路,哼。
为什么买mini而不是macbook系列,当然是为了减少一下尝鲜的成本,mini对于有显示器有键盘的童鞋来说,应该是尝鲜m1芯片最有性价比的一款了(某宝只需4700)。
另外需要说明一点,M1这个Apple Silicon虽然是arm构架,和之前使用的ipad类似,但是性能相比ipad的A12Z提升是非常大的。具体的跑分这里不展示了,全网随便搜搜就有,也就图个乐,还是需要看看平常使用的一些情况。
简单的看一下包装盒子。其实这个mini没见到实物前看起来不大,但实际拿在手中还是感觉挺大的,起码装到书包里也是不能忽视的一大个。
拆开看看后面的接口数量,对我来说接口多少其实不是很重要,满足基本要求就好,实在不行就拓展坞。接显示器的话,HDMI链接4K屏就很完美。
展示一下略显凌乱的桌面,键盘是IKBC的静音红轴,显示器是LG的27UL550,27寸4k,虽然不是4k的最佳尺寸,显示程度也比较细腻了,算是入门级4k屏幕。
显示分辨率设置为2304 x 1296 60HZ刚刚好,毕竟原生4k看的眼睛会瞎😓,需要注意30HZ和60HZ对鼠标流畅度影响很大,之前mbp2017在链接4k屏的时候30hz的刷新率用起来太不舒服了。
使用体验
使用了一个多月,大部分情况和平常使用几乎没有区别,对于我来说就是VSCODE
+Pycharm
+一些其他的工具(paste
、esayconnect
、iterm2
等),使用起来和平常区别不是很大,前提是需要稍微花一点心思去折腾下。甚至如果不需要ide的话,直接iterm+vim插件就能解决绝大部分编译代码和使用场景。
还有一些常用软件,迅雷、QQ、微信、钉钉、爱奇艺啥的都没问题,其中有的是转译有的是原生支持,目前用起来没有明显区别。放心大胆地用吧!查看各类软件对M1芯片的支持程度:https://doesitarm.com/ 目前是1月10号,绝大部分的软件都已经支持的差不多了。
M1芯片的使用报告网上很多,我这里就不赘述啦,只挑我比较感兴趣的方面来说说吧。
最新消息
Pycharm和Clion在1月2号的最新更新已经原生支持了Apple Silicon(他们公司全家的产品应该都支持M1了),简单尝试了下,ZNM丝滑。
CPU性能
简单测试一下M1芯片8核CPU的性能,以下代码使用的库为Pytorch,做矩阵加法运算(代码借鉴于 https://github.com/pytorch/pytorch/issues/48145):
from tqdm import tqdm
import torch
@torch.jit.script
def foo():
x = torch.ones((1024 * 12, 1024 * 12), dtype=torch.float32).cuda()
y = torch.ones((1024 * 12, 1024 * 12), dtype=torch.float32).cuda()
z = x + y
return z
if __name__ == '__main__':
z0 = None
for _ in tqdm(range(10000000000)):
zz = foo()
if z0 is None:
z0 = zz
else:
z0 += zz
上面这段代码在1080ti上运行这段代码的速度为325,通过nvidia-smi命令可以看到GPU已经被打满了。
0%| | 11936/10000000000 [00:44<8543:10:59, 325.15it/s]
同样,使用M1芯片的CPU跑这段代码(去掉上述的cuda()
),结果为45,同样CPU已经被打满了。
两者的差距差不多为7倍,不过其实这段代码是有问题的,没有考虑在1080TI上数据从CPU到GPU传输问题(而M1不计传输耗时),因此不是客观对CPU的性能比较,看个热闹就行~。PS:我真的不会拿CPU进行训练的!
期待之后Pytorch能够运行在M1芯片的GPU上(要靠pytorch官方人员推动还是很难,毕竟官方开发者很忙需要专注其他方向,还是需要其他开源开发者的力量)。
关于M1芯片与2080TI的速度比较,还有一篇文章比较有意思:M1 Mac Mini Scores Higher Than My RTX 2080Ti in TensorFlow Speed Test.
在M1上编译pytorch
目前在M1上正常使用Pytorch需要使用arm版本的conda环境编译,arm版本的conda下载地址如下:https://conda-forge.org/blog/posts/2020-10-29-macos-arm64/
安装完上述miniconda后即可按照以下步骤编译安装Pytorch:
https://github.com/pytorch/pytorch/issues/48145
这里也提供直接编译好的torch-1.8.0a0-cp38-cp38-macosx_11_0_arm64.whl
:
链接: https://pan.baidu.com/s/10WSazrv3V-Nq5vCQ7Rmh9w 密码: ipp0
额外的参考链接:http://iphonesdkdev.blogspot.com/2020/11/202011.html
Neural Engine
其实M1芯片对我吸引最大的就是其中的神经网络引擎(之后简称ANE):
神经网络引擎,也就是neural engine
,最开始出现在A11 Bionic
也就是iphoneX/8使用的芯片,不过那个时候这个引擎只用于face id
和Animoji
。后来到了A12 Bionic
才可能被开发者通过Core ML部署到手机上,再到后来的A13 Bionic
、A14 Bionic
,一代更比一代强。
到了M1芯片使用的neural enging貌似和A14 Bionic
一样是16和至多11tflops/s的计算能力,要知道当年的GTX TAITAN X
也刚刚11TFlops,不过当然这两者的计算精度是不一样的。ANE只支持fp16和(u)int8类型数据的计算。
关于ANE具体的细节可以看这里.
coremltools
最简单调用苹果neural engine的方式是使用coremltools来运行,第一步当然是先安装coremltools!从官方GITHUB克隆下来然后执行:
1. cd to root of coremltools
2. mkdir build && cd build
3. cmake ..
4. make install
5. python setup.py install
建议自己编译,直接使用pip应该也可以安装(安装后需要检查一下在python的site-package中是否有libcoremlpython.so
)。
import numpy as np
import coremltools as ct
from coremltools.models.neural_network import datatypes, NeuralNetworkBuilder
input_features = [('image', datatypes.Array(3))]
output_features = [('probs', datatypes.Array(3))]
weights = np.zeros((3, 3)) + 3
bias = np.ones(3)
builder = NeuralNetworkBuilder(input_features, output_features)
builder.add_inner_product(name='ip_layer', W=weights, b=None, input_channels=3, output_channels=3, has_bias=False, input_name='image', output_name='med')
builder.add_bias(name='bias', b=bias, input_name='med', output_name='probs', shape_bias=(3,))
mlmodel = ct.models.MLModel(builder.spec)
# 实际执行的时候使用了ANE
out = mlmodel.predict({"image": np.array([1337,0,0], dtype=np.float32)})
print(out)
运行上面这段代码就可以调用ANE引擎,呃。怎么知道调用了捏。
观察
我们通过dmesg
来观察ANE是否被调用。
dmesg
命令可以检测和控制内核环缓冲,我们可以通过这个来了解系统的启动信息,也可以通过这个命令查看mac系统是否调用了neural engine
。
执行以下命令观察窗口,当系统调用neural engine的时候会打印相关信息:
watch -n 0.1 'sudo dmesg | grep H11'
然后运行上述的.py
代码。
python coreml_ane.py
{'probs': array([4012., 4012., 4012.])}
可以看到输出结果,同时我们也可以看到刚才watch dmesg
的信息:
[14453.207863]: Sandbox: ContextStoreAgen(482) deny(1) mach-lookup com.apple.ocspdvirtual IORetu
rn H11ANEIn::newUserClient(task_t, void *, UInt32, IOUserClient **) : H11ANEIn::newUserClient ty
pe=2
[14453.228654]: virtual IOReturn H11ANEIn::newUserClient(task_t, void *, UInt32, IOUserClient **
) : H11ANEIn::newUserClient : Creating default full-entitlement client
[14453.228663]: virtual bool H11ANEInUserClient::init(task_t, OSDictionary *) - New UserClient f
or process: aned (pid 6887)
[14453.228720]: IOReturn H11ANEInUserClient::ANE_PowerOn() - client aned requesting Power On
[14453.228723]: IOReturn H11ANEIn::ANE_PowerOn_gated(void *, const char *, bool) : H11ANEIn::Pow
ering on ANE
[14453.228728]: IOReturn H11ANEIn::ANE_PowerOn_gated(void *, const char *, bool) : H11ANEIn::AN
E_PowerOn_gated - Wait until ANE gets powered up for client <ptr> retries=1
[14453.228775]: IOReturn H11ANEIn::setPowerStateGated(unsigned long, IOService *) : H11ANEIn::se
tPowerStateGated: 1
[14453.234362]: H11ANEIn::power_on_hardware - FW App image...
[14453.252851]: IOReturn H11ANEIn::ANE_Init(): Statistics: ColdStarts: 7, JetsamTriggeredColdSta
rts: 0, Resumes: 0, ResumesFailed: 0, SuspendsSuccessful: 0, SuspendsFailed: 0 FirmwareTimeouts:
0 ANEDeInits: 6 ANEInitFailures: 0
[14453.252864]: IOReturn H11ANEIn::ANE_Init(): Work Stats: WorkSubmitted: 6 WorkBegin: 6 WorkEn
ded: 6 PendingRequests: 0
[14453.253097]: H11ANEIn: ANE_ProgramCreate_gated:, ZinComputeProgramMake, get Mcache size: 0x0
[14453.253100]: H11ANEIn: ANE_ProgramCreate_gated:,Program Identifier:ANEC v1
[14453.253108]: IOReturn H11ANEIn::ANE_ProgramCreate_gated(H11ANEProgramCreateArgs *, H11ANEProg
ramCreateArgsOutput *, H11ANEProgramCreateArgsAdditionalParams *) : H11ANEIn::kernel is non-muta
ble kernel section
[14453.253162]: IOReturn H11ANEIn::ANE_ProgramCreate_gated(H11ANEProgramCreateArgs *, H11ANEProg
ramCreateArgsOutput *, H11ANEProgramCreateArgsAdditionalParams *) : WARN: H11ANEIn: Intermediate
buffer size is zero
[14453.253342]: IOReturn H11ANEIn::ANE_ProcessCreate_gated(H11ANEProcessCreateArgs *, H11ANEProc
essCreateArgsOutput *) : programBuffer programHandle = 0x50c38b4fa8 programId = 0
[14453.254432]: virtual IOReturn H11ANEIn::newUserClient(task_t, void *, UInt32, IOUserClient **
) : H11ANEIn::newUserClient type=1
[14453.254434]: virtual IOReturn H11ANEIn::newUserClient(task_t, void *, UInt32, IOUserClient **
) : H11ANEIn::newUserClient : Creating direct evaluate client
[14453.254438]: virtual bool H11ANEInDirectPathClient::init(task_t, OSDictionary *) - New UserCl
ient for process: python3.8 (pid 63314)
[14453.286145]: IOReturn H11ANEIn::FreeIntermediateBuffer(H11ANEIntermediateBufferSurfaceParams
*, bool): Passing NULL for intemediate buffer. Returning from here
[14453.286163]: IOReturn H11ANEIn::ANE_ProcessDestroy_gated(H11ANEProcessDestroyArgs *, bool, bo
重点看上述ANE的部分,可以看到H11ANEInUserClient::ANE_PowerOn()
->H11ANEIn::ANE_Init()
->ANE_ProcessCreate_gated
->H11ANEIn::FreeIntermediateBuffer
->ANE_ProcessDestroy_gated
的过程。
如果调用失败会打印(这种情况在没有进行授权的时候执行会出现):
[14822.089254]: AMFI: Denying core dump for pid 73626 (a.out)Sandbox: 5 duplicate reports for Co
ntextStoreAgen deny(1) mach-lookup com.apple.ocspdSandbox: bird(516) deny(1) file-read-data /Use
rs/guoyanzongFailed to write key 1950826800 to SMC with error code 86Failed to write key 1950826
829 to SMC with error code 86Failed to write key 1950826801 to SMC with error code 86Failed to w
rite key 1950829892 to SMC with error code 86virtual IOReturn H11ANEIn::newUserClient(task_t, vo
id *, UInt32, IOUserClient **) : H11ANEIn::newUserClient type=2
[14822.989968]: virtual IOReturn H11ANEIn::newUserClient(task_t, void *, UInt32, IOUserClient **
) : H11ANEIn::newUserClient : Creating default full-entitlement client
[14822.989977]: virtual bool H11ANEInUserClient::init(task_t, OSDictionary *) - process a.out (p
id 73673) denied access
提取动态链接库
简单提一下如果如果想要在外部使用M1的ANE(而不是通过coremltools的方式),可以参考参考tinygrad的ANE部分(不是很成熟),作者提取了MAC系统的dyld_shared_cache_arm64e
,通过反编译可以得到dyld_shared_cache_arm64e
中具体调用的ANEServices
动态链接库:
strings dyld_shared_cache_arm64e | grep ANEServices
/System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices
/System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices
H11ANEServicesThread
/System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices
/System/Library/PrivateFrameworks/ANEServices.framework/ANEServices
__ZN6H11ANEL25H11ANEServicesThreadStartEPNS_26H11ANEServicesThreadParamsE
/System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices
/System/Library/PrivateFrameworks/ANEServices.framework/ANEServices
ANEServices
Versions/A/ANEServices
/System/iOSSupport/System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices
提取动态链接库的仓库如下:
https://github.com/madordie/dsc_extractor
按照readme中的方式按步骤进行提取即可,我们一般需要ANECompiler, ANEServices,AppleNeuralEngine,CoreML,Espresso
这几个。
具体调用堆栈为:libcoremlpython.so
-> CoreML
-> Espresso
-> AppleNeuralEngine
-> ANEServices
。
具体在外部调用ANE的方式这里就不详细介绍了...比较复杂需要另开一篇来讲。
关于ANE的一些了解也可以看看这个:
https://www.slideshare.net/kstan2/why-you-cannot-use-neural-engine-to-run-your-nn-models-on-a11-devices
brew
homebrew可以通过转译的方式安装,直接执行,使用命令:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
可以直接安装,之后在运行brew之前加上arch -x86_64
就可以,例如:
arch -x86_64` brew install opencv
需要注意的是通过这种方式brew安装的库默认是x86架构的!如果在编译过程中链接x86架构的库会出现库构架不匹配的问题。
2021-01-31更新:
原生的brew前一阵子已经可以使用了,支持了大部分已经arm64编译好的库。并且可以和Intel版本的共存,具体可以看这篇:
https://noahpeeters.de/posts/apple-silicon/homebrew-setup/
VSCODE
VSCODE目前还是预览版本(原生支持M1),预览版是黄色的!大部分插件可以通过转译的方式正常工作。
不过cpp-tools
这个插件目前还是x86的,需要通过转译来跑:
Allow extension's x64 binaries to run on Apple Silicon x64 emulator
游戏
游戏方面,目前只玩了LOL云游戏版本的,使用腾讯的START客户端,有MAC版本的,目前公测免费。
意外的流畅,玩起来和本地玩几乎没有区别,可能是wifi-6的原因,我家100M的网可以派上用场,我还是使用无线网玩的,顺便放个视频看,玩起来毫无压力。
看了一些其他人使用window虚拟机也可以玩LOL,随着这类软件的逐渐完善,在MAC上跑window过段时间就会渐渐完美了。
查看使用的库是否为arm架构
使用命令:
lipo -info xxx
可以查看当前使用的可执行文件或者动态链接库是否为Arm架构,确保使用正确结构的软件。举个例子,在MAC上直接编译Pytorch源码,编译后可以查看_C.cpython-38-darwin.so
是否为arm架构:
@bogon torch % lipo -info _C.cpython-38-darwin.so
// x86架构 在m1上无法正常运行
Non-fat file: _C.cpython-38-darwin.so is architecture: x86_64
// arm架构
Architectures in the fat file: _C.cpython-38-darwin.so are: x86_64 arm64
所以说,M1芯片如果遇到无法正确运行的可执行文件或者动态链接库,先用这个命令看看是否为ARM架构吧!
遇到的一些小问题
还有一些小bug(可能之后会解决,但是目前还存在):
- mac mini的hdmi接到显示器偶尔会突然卡主,其实系统并没有卡而是显示器卡了,重新插拔一下显示器接口或者切换一些显示源即可
- 使用paste的时候会有卡主的现象
目前系统是Big Sur Version 11.1
。
后记
暂时就这些,对于M1芯片的Mac-mini来说,一切与x86芯片的mbp使用起来没有任何区别。除了在编译链接一些源码时需要注意构架问题,麻烦些折腾些,但这不也正是程序员的快乐所在吗?
交流
如果你与我志同道合于此,老潘很愿意与你交流;如果你喜欢老潘的内容,欢迎关注和支持。博客每周更新一篇深度原创文,关注公众号「oldpan博客」不错过最新文章。老潘也会整理一些自己的私藏,希望能帮助到大家,公众号回复"888"获取老潘学习路线资料与文章汇总,还有更多等你挖掘。如果不想错过老潘的最新推文,请点击神秘链接。