嘿嘿大家好我是老潘~
前一陣子忍不住剁手買了M1芯片的mac mini制市,為了彌補(bǔ)自己的內(nèi)疚感就賣了自己的舊的mbp2017款抬旺。數(shù)據(jù)也完全遷移到了新機(jī)器上,之前的工作也就由mbp2017徹底換成mac mini了祥楣,要換就換徹底點(diǎn)开财,不要給自己了留后路,哼误褪。
為什么買mini而不是macbook系列责鳍,當(dāng)然是為了減少一下嘗鮮的成本,mini對于有顯示器有鍵盤的童鞋來說兽间,應(yīng)該是嘗鮮m1芯片最有性價(jià)比的一款了(某寶只需4700)历葛。
另外需要說明一點(diǎn),M1這個Apple Silicon雖然是arm構(gòu)架嘀略,和之前使用的ipad類似恤溶,但是性能相比ipad的A12Z提升是非常大的乓诽。具體的跑分這里不展示了,全網(wǎng)隨便搜搜就有咒程,也就圖個樂鸠天,還是需要看看平常使用的一些情況。
簡單的看一下包裝盒子帐姻。其實(shí)這個mini沒見到實(shí)物前看起來不大稠集,但實(shí)際拿在手中還是感覺挺大的衫冻,起碼裝到書包里也是不能忽視的一大個饵沧。
拆開看看后面的接口數(shù)量,對我來說接口多少其實(shí)不是很重要扛伍,滿足基本要求就好,實(shí)在不行就拓展塢词裤。接顯示器的話刺洒,HDMI鏈接4K屏就很完美。
展示一下略顯凌亂的桌面吼砂,鍵盤是IKBC的靜音紅軸逆航,顯示器是LG的27UL550,27寸4k渔肩,雖然不是4k的最佳尺寸因俐,顯示程度也比較細(xì)膩了,算是入門級4k屏幕周偎。
顯示分辨率設(shè)置為2304 x 1296 60HZ剛剛好抹剩,畢竟原生4k看的眼睛會瞎??,需要注意30HZ和60HZ對鼠標(biāo)流暢度影響很大蓉坎,之前mbp2017在鏈接4k屏的時(shí)候30hz的刷新率用起來太不舒服了澳眷。
使用體驗(yàn)
使用了一個多月,大部分情況和平常使用幾乎沒有區(qū)別蛉艾,對于我來說就是VSCODE
+Pycharm
+一些其他的工具(paste
钳踊、esayconnect
、iterm2
等)勿侯,使用起來和平常區(qū)別不是很大拓瞪,前提是需要稍微花一點(diǎn)心思去折騰下。甚至如果不需要ide的話助琐,直接iterm+vim插件就能解決絕大部分編譯代碼和使用場景祭埂。
還有一些常用軟件,迅雷弓柱、QQ沟堡、微信侧但、釘釘、愛奇藝啥的都沒問題航罗,其中有的是轉(zhuǎn)譯有的是原生支持禀横,目前用起來沒有明顯區(qū)別。放心大膽地用吧粥血!查看各類軟件對M1芯片的支持程度:https://doesitarm.com/ 目前是1月10號柏锄,絕大部分的軟件都已經(jīng)支持的差不多了。
M1芯片的使用報(bào)告網(wǎng)上很多复亏,我這里就不贅述啦趾娃,只挑我比較感興趣的方面來說說吧。
最新消息
Pycharm和Clion在1月2號的最新更新已經(jīng)原生支持了Apple Silicon(他們公司全家的產(chǎn)品應(yīng)該都支持M1了)缔御,簡單嘗試了下抬闷,ZNM絲滑。
CPU性能
簡單測試一下M1芯片8核CPU的性能耕突,以下代碼使用的庫為Pytorch笤成,做矩陣加法運(yùn)算(代碼借鑒于 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上運(yùn)行這段代碼的速度為325,通過nvidia-smi命令可以看到GPU已經(jīng)被打滿了眷茁。
0%| | 11936/10000000000 [00:44<8543:10:59, 325.15it/s]
同樣炕泳,使用M1芯片的CPU跑這段代碼(去掉上述的cuda()
),結(jié)果為45上祈,同樣CPU已經(jīng)被打滿了培遵。
兩者的差距差不多為7倍,不過其實(shí)這段代碼是有問題的登刺,沒有考慮在1080TI上數(shù)據(jù)從CPU到GPU傳輸問題(而M1不計(jì)傳輸耗時(shí))籽腕,因此不是客觀對CPU的性能比較,看個熱鬧就行~塘砸。PS:我真的不會拿CPU進(jìn)行訓(xùn)練的节仿!
期待之后Pytorch能夠運(yùn)行在M1芯片的GPU上(要靠pytorch官方人員推動還是很難,畢竟官方開發(fā)者很忙需要專注其他方向掉蔬,還是需要其他開源開發(fā)者的力量)廊宪。
關(guān)于M1芯片與2080TI的速度比較,還有一篇文章比較有意思:M1 Mac Mini Scores Higher Than My RTX 2080Ti in TensorFlow Speed Test.
在M1上編譯pytorch
目前在M1上正常使用Pytorch需要使用arm版本的conda環(huán)境編譯女轿,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
其實(shí)M1芯片對我吸引最大的就是其中的神經(jīng)網(wǎng)絡(luò)引擎(之后簡稱ANE):
神經(jīng)網(wǎng)絡(luò)引擎箭启,也就是neural engine
,最開始出現(xiàn)在A11 Bionic
也就是iphoneX/8使用的芯片蛉迹,不過那個時(shí)候這個引擎只用于face id
和Animoji
傅寡。后來到了A12 Bionic
才可能被開發(fā)者通過Core ML部署到手機(jī)上,再到后來的A13 Bionic
、A14 Bionic
荐操,一代更比一代強(qiáng)芜抒。
到了M1芯片使用的neural enging貌似和A14 Bionic
一樣是16和至多11tflops/s的計(jì)算能力,要知道當(dāng)年的GTX TAITAN X
也剛剛11TFlops托启,不過當(dāng)然這兩者的計(jì)算精度是不一樣的宅倒。ANE只支持fp16和(u)int8類型數(shù)據(jù)的計(jì)算。
關(guān)于ANE具體的細(xì)節(jié)可以看這里.
coremltools
最簡單調(diào)用蘋果neural engine的方式是使用coremltools來運(yùn)行屯耸,第一步當(dāng)然是先安裝coremltools拐迁!從官方GITHUB克隆下來然后執(zhí)行:
1. cd to root of coremltools
2. mkdir build && cd build
3. cmake ..
4. make install
5. python setup.py install
建議自己編譯,直接使用pip應(yīng)該也可以安裝(安裝后需要檢查一下在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)
# 實(shí)際執(zhí)行的時(shí)候使用了ANE
out = mlmodel.predict({"image": np.array([1337,0,0], dtype=np.float32)})
print(out)
運(yùn)行上面這段代碼就可以調(diào)用ANE引擎线召,呃。怎么知道調(diào)用了捏多矮。
觀察
我們通過dmesg
來觀察ANE是否被調(diào)用缓淹。
dmesg
命令可以檢測和控制內(nèi)核環(huán)緩沖,我們可以通過這個來了解系統(tǒng)的啟動信息工窍,也可以通過這個命令查看mac系統(tǒng)是否調(diào)用了neural engine
割卖。
執(zhí)行以下命令觀察窗口前酿,當(dāng)系統(tǒng)調(diào)用neural engine的時(shí)候會打印相關(guān)信息:
watch -n 0.1 'sudo dmesg | grep H11'
然后運(yùn)行上述的.py
代碼患雏。
python coreml_ane.py
{'probs': array([4012., 4012., 4012.])}
可以看到輸出結(jié)果,同時(shí)我們也可以看到剛才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
重點(diǎn)看上述ANE的部分罢维,可以看到H11ANEInUserClient::ANE_PowerOn()
->H11ANEIn::ANE_Init()
->ANE_ProcessCreate_gated
->H11ANEIn::FreeIntermediateBuffer
->ANE_ProcessDestroy_gated
的過程淹仑。
如果調(diào)用失敗會打印(這種情況在沒有進(jìn)行授權(quán)的時(shí)候執(zhí)行會出現(xiàn)):
[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
提取動態(tài)鏈接庫
簡單提一下如果如果想要在外部使用M1的ANE(而不是通過coremltools的方式),可以參考參考tinygrad的ANE部分(不是很成熟)肺孵,作者提取了MAC系統(tǒng)的dyld_shared_cache_arm64e
匀借,通過反編譯可以得到dyld_shared_cache_arm64e
中具體調(diào)用的ANEServices
動態(tài)鏈接庫:
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
提取動態(tài)鏈接庫的倉庫如下:
https://github.com/madordie/dsc_extractor
按照readme中的方式按步驟進(jìn)行提取即可,我們一般需要ANECompiler, ANEServices,AppleNeuralEngine,CoreML,Espresso
這幾個平窘。
具體調(diào)用堆棧為:libcoremlpython.so
-> CoreML
-> Espresso
-> AppleNeuralEngine
-> ANEServices
吓肋。
具體在外部調(diào)用ANE的方式這里就不詳細(xì)介紹了...比較復(fù)雜需要另開一篇來講。
關(guān)于ANE的一些了解也可以看看這個:
https://www.slideshare.net/kstan2/why-you-cannot-use-neural-engine-to-run-your-nn-models-on-a11-devices
brew
homebrew可以通過轉(zhuǎn)譯的方式安裝瑰艘,直接執(zhí)行是鬼,使用命令:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
可以直接安裝,之后在運(yùn)行brew之前加上arch -x86_64
就可以紫新,例如:
arch -x86_64` brew install opencv
需要注意的是通過這種方式brew安裝的庫默認(rèn)是x86架構(gòu)的均蜜!如果在編譯過程中鏈接x86架構(gòu)的庫會出現(xiàn)庫構(gòu)架不匹配的問題。
2021-01-31更新:
原生的brew前一陣子已經(jīng)可以使用了芒率,支持了大部分已經(jīng)arm64編譯好的庫囤耳。并且可以和Intel版本的共存,具體可以看這篇:
https://noahpeeters.de/posts/apple-silicon/homebrew-setup/
VSCODE
VSCODE目前還是預(yù)覽版本(原生支持M1),預(yù)覽版是黃色的充择!大部分插件可以通過轉(zhuǎn)譯的方式正常工作德玫。
不過cpp-tools
這個插件目前還是x86的,需要通過轉(zhuǎn)譯來跑:
Allow extension's x64 binaries to run on Apple Silicon x64 emulator
游戲
游戲方面椎麦,目前只玩了LOL云游戲版本的化焕,使用騰訊的START客戶端,有MAC版本的铃剔,目前公測免費(fèi)撒桨。
意外的流暢,玩起來和本地玩幾乎沒有區(qū)別键兜,可能是wifi-6的原因凤类,我家100M的網(wǎng)可以派上用場,我還是使用無線網(wǎng)玩的普气,順便放個視頻看谜疤,玩起來毫無壓力。
看了一些其他人使用window虛擬機(jī)也可以玩LOL现诀,隨著這類軟件的逐漸完善夷磕,在MAC上跑window過段時(shí)間就會漸漸完美了。
查看使用的庫是否為arm架構(gòu)
使用命令:
lipo -info xxx
可以查看當(dāng)前使用的可執(zhí)行文件或者動態(tài)鏈接庫是否為Arm架構(gòu)仔沿,確保使用正確結(jié)構(gòu)的軟件坐桩。舉個例子,在MAC上直接編譯Pytorch源碼封锉,編譯后可以查看_C.cpython-38-darwin.so
是否為arm架構(gòu):
@bogon torch % lipo -info _C.cpython-38-darwin.so
// x86架構(gòu) 在m1上無法正常運(yùn)行
Non-fat file: _C.cpython-38-darwin.so is architecture: x86_64
// arm架構(gòu)
Architectures in the fat file: _C.cpython-38-darwin.so are: x86_64 arm64
所以說绵跷,M1芯片如果遇到無法正確運(yùn)行的可執(zhí)行文件或者動態(tài)鏈接庫,先用這個命令看看是否為ARM架構(gòu)吧成福!
遇到的一些小問題
還有一些小bug(可能之后會解決碾局,但是目前還存在):
- mac mini的hdmi接到顯示器偶爾會突然卡主,其實(shí)系統(tǒng)并沒有卡而是顯示器卡了奴艾,重新插拔一下顯示器接口或者切換一些顯示源即可
- 使用paste的時(shí)候會有卡主的現(xiàn)象
目前系統(tǒng)是Big Sur Version 11.1
净当。
后記
暫時(shí)就這些,對于M1芯片的Mac-mini來說蕴潦,一切與x86芯片的mbp使用起來沒有任何區(qū)別像啼。除了在編譯鏈接一些源碼時(shí)需要注意構(gòu)架問題,麻煩些折騰些品擎,但這不也正是程序員的快樂所在嗎埋合?
交流~
如果你與我志同道合于此,老潘很愿意與你交流萄传;如果你喜歡老潘的內(nèi)容甚颂,歡迎關(guān)注和支持蜜猾。博客每周更新一篇深度原創(chuàng)文,關(guān)注「oldpan博客」不錯過最新文章振诬。老潘也會整理一些自己的私藏蹭睡,希望能幫助到大家,公眾號回復(fù)"888"獲取老潘學(xué)習(xí)路線資料與文章匯總赶么,還有更多等你挖掘肩豁。如果不想錯過老潘的最新推文,請點(diǎn)擊神秘鏈接辫呻。