常駐AIのRaspberry Pi 5、電気代は月50〜74円と見積もった。最初の式は21Wと出て使えなかった

常駐AIのRaspberry Pi 5、電気代は月50〜74円と見積もった。最初の式は21Wと出て使えなかった

21.4W。Raspberry Pi 5に負荷をかけて、最初に計算で出てきた消費電力です。

他の人が測った、同じPi 5(4GB・8GB版)の高負荷時の値は9.8Wでした。倍以上ずれています。計算式のどこかが間違っていると、この時点で分かりました。

月のAI費用が$314だった話は先週書きました。その金額に、AIエージェントを住まわせているこのRaspberry Pi 5の電気代は入っていません。入っていないだけで、ゼロではありません。いくらなのかを一度出しておくことにしました。

手元の機械と、測り方の前提

測ったのは、稼働43日のRaspberry Pi 5(16GB)です。ストレージはNVMeが466GBとmicroSDが115GB。USB機器は何もつないでいません。

この機械では、AIエージェントのゲートウェイ、コーディングエージェント数本、ブラウザが動いたままです。測定中は、AIが記事を書く自動ジョブも同じ機械で走っていました。測定開始時のロードアベレージは1.77。つまり「何もしていない状態」ではなく、「いつもの常駐状態」を測っています。

今回はワット計を使っていません。代わりに、Pi 5が自分で持っている電圧と電流のセンサーを使います。電源管理用のチップ(PMIC)が各所の電圧と電流を測っていて、vcgencmd pmic_read_adc で読めます。Raspberry Piの公式ドキュメントにも、このコマンドの記載があります。

vcgencmd pmic_read_adc の出力(そのまま)
vcgencmd pmic_read_adc
 3V7_WL_SW_A current(0)=0.00097593A
   3V3_SYS_A current(1)=0.14638950A
   1V8_SYS_A current(2)=0.19616190A
  DDR_VDD2_A current(3)=0.01659081A
  DDR_VDDQ_A current(4)=0.00000000A
   1V1_SYS_A current(5)=0.17566740A
    0V8_SW_A current(6)=0.28594750A
  VDD_CORE_A current(7)=0.63403000A
   3V3_DAC_A current(17)=0.00024420A
   3V3_ADC_A current(18)=0.00030525A
   0V8_AON_A current(16)=0.00311355A
      HDMI_A current(22)=0.01953600A
 3V7_WL_SW_V volt(8)=3.71184000V
   3V3_SYS_V volt(9)=3.30724900V
   1V8_SYS_V volt(10)=1.81684800V
  DDR_VDD2_V volt(11)=1.10402800V
  DDR_VDDQ_V volt(12)=0.60586020V
   1V1_SYS_V volt(13)=1.10549400V
    0V8_SW_V volt(14)=0.80109810V
  VDD_CORE_V volt(15)=0.89638500V
   3V3_DAC_V volt(20)=3.31226800V
   3V3_ADC_V volt(21)=3.30494200V
   0V8_AON_V volt(19)=0.79999920V
      HDMI_V volt(23)=5.10272000V
     EXT5V_V volt(24)=5.09066000V
      BATT_V volt(25)=0.00341880V

出力は12本の電流(A)と14本の電圧(V)です。頭の名前が同じものを、電流と電圧で1組にできます。たとえば VDD_CORE_A はCPUコアへ流れる電流で、VDD_CORE_V がその電圧です。このように電圧ごとに分かれた電源の線を「レール」と呼びます。

電流が12本あるのに、EXT5V_V(USB-Cから入ってくる5Vの電圧)には対応する電流がありません。ここが、後で効いてきます。

式を2つ並べて走らせた

電力は電圧×電流です。ただ、入口の電流が読めないので、式の作り方が2通り考えられました。

  • 式A:読めた電流を全部足して、EXT5V_V(約5.05V)を掛ける。入口の電圧で全部まとめて計算します。
  • 式B:レールごとに電圧×電流を出し、最後に足す。

どちらが正しいのか、書く前には決められませんでした。同じスクリプトへ両方入れて、1秒間隔で60回測ります。

import subprocess, re, time

def read():
    out = subprocess.run(["vcgencmd", "pmic_read_adc"],
                         capture_output=True, text=True).stdout
    cur, vol = {}, {}
    for line in out.splitlines():
        m = re.match(r"\s*(\S+)_([AV]) (current|volt)\(\d+\)=([\d.]+)[AV]", line)
        if m:
            (cur if m.group(2) == "A" else vol)[m.group(1)] = float(m.group(4))
    return cur, vol

cur, vol = read()
per_rail = sum(cur[k] * vol[k] for k in cur if k in vol)   # 式B
all_5v   = sum(cur.values()) * vol["EXT5V"]                # 式A

実際のスクリプトは、これを60回まわして平均・最小・最大をJSONで出します。名前は sample.py です。まず、いつもの常駐状態で測りました。

python3 sample.py resident_asis 60
{"label": "resident_asis", "n": 60, "per_rail_W": 2.2885684223646763, "all5v_W": 9.225510736475456, "ext5v_V": 5.05025895, "sum_A": 1.8275344365, "min_per_rail": 1.68517792393521, "max_per_rail": 3.88705305681179}

式B(per_rail_W)は2.29W、式A(all5v_W)は9.23Wです。4倍も違います。常駐状態で9Wは、他の人の計測した負荷時の値9.8Wにほぼ並んでしまうので、すでに怪しい数字でした。

4コアを回すと、式Aが壊れた

次に負荷をかけます。4本の while True: pass を nice -n 19 で起動し、5秒待ってから55回測りました。

python3 burn.py
{"label": "burn4_60s", "n": 55, "per_rail_W": 4.452456292659316, "all5v_W": 21.394008458978664, "ext5v_V": 5.043930527272727, "sum_A": 4.241771681272727, "min_per_rail": 4.21677765346768, "max_per_rail": 5.4823155932877}
frequency(0)=2400037120
temp=55.4'C
throttled=0x0

式Aは21.39Wです。冒頭の数字はここから来ました。21Wは、公式が推奨する5V/5A電源(25W)の上限に迫る値です。ところが、CPUは2.4GHzのまま、温度は55.4℃、throttled=0x0(電圧低下などによる制限なし)。そこまで引いている様子は、クロックと温度のどちらにも出ていません。

原因は式Aの前提です。電流の多くは、1V前後の低い電圧のレールを流れています。たとえば VDD_CORE は約0.9Vです。それに5Vを掛けると、電力が5倍以上に膨らみます。入口の5Vから見れば、降圧された先の電流は、もっと小さい電流として流れ込んでいるはずです。電流を足すこと自体が、そもそも間違っていました。

式Bは4.45Wでした。常駐状態との差は約2.2Wで、4コアが全部回ったときの上乗せとして、桁は自然です。

負荷をやめて15秒おいてから、もう一度、常駐状態を60回測りました。

python3 sample.py resident_asis_after 60
{"label": "resident_asis_after", "n": 60, "per_rail_W": 2.223309613742779, "all5v_W": 8.927168650723049, "ext5v_V": 5.0513756333333335, "sum_A": 1.7678551693333333, "min_per_rail": 1.6938427978633899, "max_per_rail": 3.3426863193647796}

2.22Wです。1回目の2.29Wと、0.07W違いました。常駐中の揺れは、1.69Wから3.89Wまで見えています。平均すると約2.26Wです。

月額にすると

電気料金の単価には、家電公取協が示す目安の31円/kWh(税込)を使います。実際の契約単価ではありません。

比べる値は3つ置きました。式Bの2.26W、他の人が測った4GB・8GB版のアイドル値3.2〜3.3W、そして使えなかった式Aの約9.08Wです。

for label, w in [("PMIC合算(常駐2回平均)", (2.2886+2.2233)/2),
                 ("5V換算の誤り", (9.2255+8.9272)/2),
                 ("外部測定アイドル3.3W", 3.3),
                 ("外部測定アイドル3.2W", 3.2)]:
    kwh = w*24*30/1000
    print(label, round(w,2), "W", round(kwh,2), "kWh/30d",
          round(kwh*31,1), "円/30d", round(kwh*31*12), "円/年")
PMIC合算(常駐2回平均) 2.26 W 1.62 kWh/30d 50.4 円/30d 604 円/年
5V換算の誤り 9.08 W 6.53 kWh/30d 202.6 円/30d 2431 円/年
外部測定アイドル3.3W 3.3 W 2.38 kWh/30d 73.7 円/30d 884 円/年
外部測定アイドル3.2W 3.2 W 2.3 kWh/30d 71.4 円/30d 857 円/年

月にすると、式Bで50円、外部の値で71〜74円です。式Aを信じていたら、月203円と書くところでした。

まだ納得していない点

式Bの2.26Wは、おそらく低すぎます。理由は2つあります。

1つ目。PMICが測れるのは降圧した後のレールです。変換のロスや、PMICの外側で使われる電力は入っていません。Raspberry Pi公式フォーラムにも、pmic_read_adc の合計は実際の総消費電力と合わない、という指摘があります。

2つ目。NVMeやmicroSDがどのレールから電力を取っているのか、私は確認できていません。NVMeを積んだ機械は、積まない機械より電力を使うはずです。この2.26Wにそれが含まれているのかどうか、わかりません。

だから、月50〜74円は「見積もり」と書きます。下限が50円、上限が外部の値の74円。真ん中のどこかに本当の数字がある、という言い方が今の精一杯です。上限も、他人の別の個体の値を借りているだけです。

もう1点、負荷試験の相手は、私が書いた4本の空ループです。外部の9.8Wは stress-ng という負荷専用ツールの結果で、メモリや他の部品にも負荷をかけます。私の4.45Wと並べて「半分しか出ない」とは言えません。

次にやるなら、ACアダプタとコンセントの間にワット計を挟みます。それで下限か上限かが決まります。

電気代は、判断を変えない金額でした

見積もりのどこを取っても、年にして600〜900円です。

この数字で、Pi 5を常駐させる判断は変わりません。変わるとしたら、マシンを何台も並べたときです。10台にしても月500〜740円。ここまで来ても、ドル建てで月$314のAI費用と、桁が違います。

先週の記事の金額に電気代は入っていませんでしたが、入れても結論が変わらない大きさでした。ただし、測って初めて言えたことです。「常時稼働」と聞くと、もっと大きな金額に見えがちです。

出典

この記事をシェア