こんにちは。ナミレリです。みなさん、MacでPythonは使っていますか?
2024年3月にM3のMacbook Airが発売され、15インチのシルバーを購入しました。M1、M2、M3でPython 3.12.2のベンチマークテストを行い、どの程度シングルコア性能が上がっているのかを確認していきます。
環境は以下の通りです。macOSはSonoma 14.4でPythonのバージョンは3.12.2に統一しています。
- M3 MacBook Air 15インチ(メモリ24GB)
- macOS Sonoma 14.4
- Python 3.12.2
- M2 MacBook Air 13.6 インチ(メモリ16GB)
- macOS Sonoma 14.4
- Python 3.12.2
- MacBook Pro 14インチ M1Max(メモリ32GB)
- macOS Sonoma 14.4
- Python 3.12.2
目次
計測環境について
以下の3つのMacBookを使います。sysctl -aの詳細です。
M3 MacBook Air
sysctl -a | grep -e brand_string -e cpu.core_count
machdep.cpu.core_count: 8
machdep.cpu.brand_string: Apple M3
M2 MacBook Air
sysctl -a | grep -e brand_string -e cpu.core_count
machdep.cpu.core_count: 8
machdep.cpu.brand_string: Apple M2
M1 Max MacBook Pro
sysctl -a | grep -e brand_string -e cpu.core_count
machdep.cpu.core_count: 10
machdep.cpu.brand_string: Apple M1 Max
ベンチマークスクリプトについて
下の記事を参考にさせていただきました。
また、Pythonの3.9、3.10、3.11などのベンチマークについて以前の記事で紹介しています。ぜひご覧ください。
では、早速はじめてみます。
1. forループ処理
単純なforによるループ処理を計測してみます。0から100,000,000までの数字をforによりループさせ、それを10回繰り返す処理です。処理が終わったら平均を秒で出力します。
import timeit
def test():
for i in range(n):
pass
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
forループ処理 | 1.157 | 1.092 | 0.769 |
2. 代入処理
上の単純なforによるループ処理に加えて、ans = iの代入を追加したプログラムです。
0から100,000,000までの数字をforによりループさせ、都度その数値をansに代入しています。それを10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
ans = 0
for i in range(n):
ans = i
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
代入処理 | 1.311 | 1.099 | 0.864 |
3. 定数を使った四則演算
0から100,000,000までの数字をforによりループさせ、都度、決まった四則演算を行っています。10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
for i in range(n):
1 + 1
1 - 1
1 * 1
1 // 1
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
定数を使った四則演算 | 1.292 | 1.100 | 0.901 |
4. 変数を使った四則演算
2から100,000,000までの数字をforによりループさせ、繰り返し変数iを使って四則演算を行っています。10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
for i in range(2, n):
i * (i % (i - 1)) + i
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
変数を使った四則演算 | 5.675 | 4.797 | 3.926 |
5. if文
0から100,000,000までの数字をforによりループさせ、if文の条件で繰り返し変数iが0だったら、という処理をしています。全体を10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
for i in range(n):
if i == 0:
pass
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
if文 | 1.615 | 1.397 | 1.087 |
配列アクセス
6. シーケンシャル
0から100,000,000までのリストAを用意して、順番にリストの要素にアクセスします。全体を10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
A = [i for i in range(n)]
for a in A:
pass
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
配列アクセス | 2.302 | 2.059 | 1.630 |
7. ランダムアクセス(Read)
0から100,000,000までのリストAを用意して、indexを指定してリストの要素にアクセスします。全体を10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
A = [i for i in range(n)]
for i in range(n):
A[i]
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
ランダムアクセス(Read) | 3.595 | 3.180 | 2.435 |
8. ランダムアクセス(Write)
0から100,000,000までのリストAを用意して、indexを指定してリストの要素にアクセスします。その要素に0を代入します。全体を10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
A = [i for i in range(n)]
for i in range(n):
A[i] = 0
n = 100000000 # 10^8
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
ランダムアクセス(Write) | 3.740 | 3.182 | 2.492 |
9. ソート
0から1,000,000までランダムな順番のリストAを用意し、その後リストAをソートします。全体を10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
from random import randint, seed
seed(0)
def test():
A = [randint(1, n) for _ in range(n)]
A.sort()
n = 1000000 # 10^6
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
ソート | 0.455 | 0.425 | 0.367 |
文字列結合
10. +による結合
0から10,000,000までの数字をforによりループさせ、都度、文字列aを結合します。10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
ans = ''
for i in range(n):
ans += 'a'
n = 10000000 # 10^7
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
+による結合 | 0.492 | 0.458 | 0.385 |
11. joinによる結合
0から10,000,000までのリストA(文字列)を用意して、リストの各要素を区切り文字なしで結合します。10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
A = [str(i) for i in range(n)]
ans = ''.join(A)
n = 10000000 # 10^7
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
joinによる結合 | 0.689 | 0.645 | 0.529 |
モジュール
12. deque
0から10,000,000までのdequeオブジェクトを用意し、0から10,000,000までの数字をforによりループさせ、dequeオブジェクトの先頭の要素を取り出し(que.popleft())、その要素を末尾に追加します(que.append())。10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
from collections import deque
def test():
que = deque(range(n))
for i in range(n):
que.append(que.popleft())
n = 10000000 # 10^7
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
deque | 0.545 | 0.426 | 0.340 |
13. 優先度付きキュー
Python標準ライブラリのheapqを使い、リストqueに0から10,000,000までを優先度付きキューの要素に挿入します。その後、優先度付きキューから最小値を取り出します。この処理を10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
import heapq
def test():
que = [0]
for i in range(n):
heapq.heappush(que, i)
for i in range(n):
heapq.heappop(que)
n = 10000000 # 10^7
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
優先度付きキュー | 2.898 | 2.756 | 2.325 |
14. 組み込み関数呼び出し
0から10,000,000までの数字をforによりループさせ、max関数により繰り返し変数iと0の2つの要素の内、最大値を持つ要素の値を取得します。この処理を10回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def test():
for i in range(n):
max(0, i)
n = 10000000 # 10^7
loop = 10
result = timeit.timeit('test()', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
組み込み関数呼び出し | 0.918 | 0.905 | 0.648 |
15. 再帰関数
40番目のフィボナッチ数列を再帰処理で求めます。3回繰り返し、処理が終わったら平均を秒で出力します。
import timeit
def fibonacci(n):
if n == 0 or n == 1:
return n
else:
return fibonacci(n - 2) + fibonacci(n - 1)
n = 40
loop = 3
result = timeit.timeit('fibonacci(n)', globals=globals(), number=loop)
print(format(result / loop, '.3f'))
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
再帰関数 | 11.374 | 10.607 | 8.754 |
numpy
16. numpy
libblasを指定したnumpyでの計測です。
import time
import numpy as np
np.random.seed(42)
a = np.random.uniform(size=(300, 300))
runtimes = 10
timecosts = []
for _ in range(runtimes):
s_time = time.time()
for i in range(100):
a += 1
np.linalg.svd(a)
timecosts.append(time.time() - s_time)
print(f'mean of {runtimes} runs: {np.mean(timecosts):.5f}s')
M1 Max MacBook Pro | M2 MacBook Air | M3 MacBook Air | |
numpy | 1.007 | 0.806 | 0.706 |
使われるBLASによって計算速度が変わります。BLASについては下記サイトがわかりやすくまとめてくれています。
Numpyの定番オススメ書籍:Pythonによるデータ分析入門 第2版
最後に
最後まで読んでいただきありがとうございます。今回の【Python】M1、M2、M3のMacbookでPython 3.12.2をベンチマークするはいかがでしたでしょうか。M1の衝撃は今でも忘れることはないですが、M2、M3と性能がしっかり向上しているのがわかります。MacでPythonは楽しいですね。
MacやLinux、Pythonなど技術系のkindle本も豊富にあります。詳しくはこちらから。
Amazonの電子書籍読み放題サービス「Kindle Unlimited」でプライム会員を対象に、最初の3か月間を無料体験できるキャンペーンを実施中。マンガ、小説、ビジネス書、雑誌など500万冊から、好きな本を何冊でも読めるキャンペーンです。
初めてkindle unlimited 読み放題をご利用の方は30日間の無料で体験できます。
期間終了後は月額980円で、いつでもキャンセルできます。
200万冊以上が読み放題。お好きな端末で利用可能です。