好久不見了各位!
前一陣子忍不住剁手買了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"獲取老潘學習路線資料與文章彙總,還有更多等你挖掘。如果不想錯過老潘的最新推文,請點選神祕連結。