Mac mini M1使用簡單體驗(編程、遊戲、深度學習)

好久不見了各位!

前一陣子忍不住剁手買了M1晶片的mac mini,為了彌補自己的內疚感就賣了自己的舊的mbp2017款。數據也完全遷移到了新機器上,之前的工作也就由mbp2017徹底換成mac mini了,要換就換徹底點,不要給自己了留後路,哼。

你懂得

為什麼買mini而不是macbook系列,當然是為了減少一下嘗鮮的成本,mini對於有顯示器有鍵盤的童鞋來說,應該是嘗鮮m1晶片最有性價比的一款了(某寶只需4700)。

另外需要說明一點,M1這個Apple Silicon雖然是arm構架,和之前使用的ipad類似,但是性能相比ipad的A12Z提升是非常大的。具體的跑分這裡不展示了,全網隨便搜搜就有,也就圖個樂,還是需要看看平常使用的一些情況。

簡單的看一下包裝盒子。其實這個mini沒見到實物前看起來不大,但實際拿在手中還是感覺挺大的,起碼裝到書包里也是不能忽視的一大個。

碩大的mac-mini盒子!

拆開看看後面的介面數量,對我來說介面多少其實不是很重要,滿足基本要求就好,實在不行就拓展塢。接顯示器的話,HDMI鏈接4K屏就很完美。

macmini介面

展示一下略顯凌亂的桌面,鍵盤是IKBC的靜音紅軸,顯示器是LG的27UL550,27寸4k,雖然不是4k的最佳尺寸,顯示程度也比較細膩了,算是入門級4k螢幕。

桌面效果

顯示解析度設置為2304 x 1296 60HZ剛剛好,畢竟原生4k看的眼睛會瞎😓,需要注意30HZ和60HZ對滑鼠流暢度影響很大,之前mbp2017在鏈接4k屏的時候30hz的刷新率用起來太不舒服了。

解析度

使用體驗

使用了一個多月,大部分情況和平常使用幾乎沒有區別,對於我來說就是VSCODE+Pycharm+一些其他的工具(pasteesayconnectiterm2等),使用起來和平常區別不是很大,前提是需要稍微花一點心思去折騰下。甚至如果不需要ide的話,直接iterm+vim插件就能解決絕大部分編譯程式碼和使用場景。

還有一些常用軟體,迅雷、QQ、微信、釘釘、愛奇藝啥的都沒問題,其中有的是轉譯有的是原生支援,目前用起來沒有明顯區別。放心大膽地用吧!查看各類軟體對M1晶片的支援程度://doesitarm.com/ 目前是1月10號,絕大部分的軟體都已經支援的差不多了。

M1晶片的使用報告網上很多,我這裡就不贅述啦,只挑我比較感興趣的方面來說說吧。

最新消息

Pycharm和Clion在1月2號的最新更新已經原生支援了Apple Silicon(他們公司全家的產品應該都支援M1了),簡單嘗試了下,ZNM絲滑。

Pycharm已經原生支援m1

CPU性能

簡單測試一下M1晶片8核CPU的性能,以下程式碼使用的庫為Pytorch,做矩陣加法運算(程式碼借鑒於 //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下載地址如下://conda-forge.org/blog/posts/2020-10-29-macos-arm64/

安裝完上述miniconda後即可按照以下步驟編譯安裝Pytorch:

//github.com/pytorch/pytorch/issues/48145

這裡也提供直接編譯好的torch-1.8.0a0-cp38-cp38-macosx_11_0_arm64.whl:

鏈接: //pan.baidu.com/s/10WSazrv3V-Nq5vCQ7Rmh9w 密碼: ipp0

額外的參考鏈接://iphonesdkdev.blogspot.com/2020/11/202011.html

Neural Engine

其實M1晶片對我吸引最大的就是其中的神經網路引擎(之後簡稱ANE):

強大的AI引擎

神經網路引擎,也就是neural engine,最開始出現在A11 Bionic也就是iphoneX/8使用的晶片,不過那個時候這個引擎只用於face idAnimoji。後來到了A12 Bionic才可能被開發者通過Core ML部署到手機上,再到後來的A13 BionicA14 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

提取動態鏈接庫的倉庫如下:

//github.com/madordie/dsc_extractor

按照readme中的方式按步驟進行提取即可,我們一般需要ANECompiler, ANEServices,AppleNeuralEngine,CoreML,Espresso這幾個。

具體調用堆棧為:libcoremlpython.so -> CoreML -> Espresso -> AppleNeuralEngine -> ANEServices

具體在外部調用ANE的方式這裡就不詳細介紹了…比較複雜需要另開一篇來講。

關於ANE的一些了解也可以看看這個:
//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 //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版本的共存,具體可以看這篇:

//noahpeeters.de/posts/apple-silicon/homebrew-setup/

VSCODE

VSCODE目前還是預覽版本(原生支援M1),預覽版是黃色的!大部分插件可以通過轉譯的方式正常工作。

VSCODE

不過cpp-tools這個插件目前還是x86的,需要通過轉譯來跑:

Allow extension’s x64 binaries to run on Apple Silicon x64 emulator

遊戲

遊戲方面,目前只玩了LOL雲遊戲版本的,使用騰訊的START客戶端,有MAC版本的,目前公測免費。

START雲遊戲

意外的流暢,玩起來和本地玩幾乎沒有區別,可能是wifi-6的原因,我家100M的網可以派上用場,我還是使用無線網玩的,順便放個影片看,玩起來毫無壓力。

WIFI6

看了一些其他人使用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″獲取老潘學習路線資料與文章匯總,還有更多等你挖掘。如果不想錯過老潘的最新推文,請點擊神秘鏈接