0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIに「速くして」と頼んだコード、10本中5本が遅くなった

0
Last updated at Posted at 2026-07-02

AIに最適化させた10本のベンチ結果:速くなった3本/変わらない2本/遅くなった5本。速くなったのは計算量を下げた3本だけで、ライブラリ導入・並列化・Pythonic化はむしろ遅くなったという図

先に結論を書きます。AIに「このコード、速くして」と頼んで出てきた10本を実測したところ、速くなったのは3本だけでした。2本は誤差の範囲で変わらず、残り5本はむしろ遅くなりました。半分が、最適化を頼んだ結果として遅くなったわけです。

「AIに最適化させれば速くなる」と私はなんとなく信じていました。コードはたしかに「それっぽく」書き換わります。内包表記になり、ライブラリが入り、見た目はモダンになる。ですが、ストップウォッチを当てると話が変わりました。

きっかけは、ある関数をAIに最適化してもらった後、なぜか体感が重くなったことです。「気のせいかな」と思って測ったら、本当に遅くなっていました。それなら他のコードはどうなんだと思い、10本まとめて検証したのがこの記事です。before/after を1本ずつ計測した、地味な記録です。

この記事は「実行速度」の話です

最初に線を引いておきます。AIにリファクタを頼むと困る話は前にもありました。「いい感じに整えて」と言ったら頼んでもいない Decimal 型や dataclass が増えていく、という品質と設計の暴走です。

この記事はそれとは別軸です。扱うのは見た目や設計ではなく、実行速度そのものです。「速くして」と明示的に頼んだのに、計測したら遅くなっていた。そこだけを数字で追います。

計測条件

数字を出す前に、条件を明記します。推測値は1つもありません。

  • マシン: MacBook Pro (M3, メモリ24GB)
  • Python: 3.12.3
  • 計測: timeit で各1,000回実行し、中央値を採用
  • メモリ: tracemalloc のピーク値
  • 対象: 私が実務や検証で書いた既存関数10本
  • 手順: 各関数を1つずつ提示し「動作を変えずに高速化して」とだけ指示

同じプロンプト、同じデータ、同じマシンで before/after を測りました。AIのモデルは2026年6月時点の主要なコーディング向けモデルを使っています。

結果: 10本のベンチ

実行時間の中央値です。短いほど速い、を意味します。

# 元コードの内容 AIの最適化方針 before after 判定
1 CSVを手書きループで集計 pandasに置換 12ms 45ms 遅化
2 リスト内包表記 ジェネレータ+map 8.2ms 9.1ms 遅化
3 二重ループで突き合わせ 辞書引きに変更 340ms 11ms 改善
4 += で文字列連結 "".join() に変更 56ms 6ms 改善
5 逐次処理 threadingで並列化 220ms 260ms 遅化
6 素朴な再帰 メモ化を追加 1,200ms 18ms 改善
7 dictで状態保持 dataclass+slots化 4.1ms 4.3ms 不変
8 小さい配列の計算 numpy導入 0.9ms 3.2ms 遅化
9 ネストしたif 早期returnに整理 15ms 15ms 不変
10 forループ itertoolsで「Pythonic」に 22ms 28ms 遅化

改善3本、不変2本、遅化5本。最適化を頼んだのに、半分が遅くなりました。改善した3本は確かに桁違いに速くなったので、平均だけ見ると速くなったように錯覚します。ですが1本ずつ見ると、半分は逆に走っていました。

「最適化した」というAIの説明文と、実際の実行時間は別物です。コードが綺麗になることと、速くなることは、まったく相関しませんでした。

速くなった3本に共通すること

改善した3本(#3、#4、#6)を見ると、共通点が1つあります。すべて 計算量そのものを下げた ケースです。

二重ループ(#3)は O(n²) を辞書引きの O(n) にしました。文字列連結(#4)は、+= のたびに新しい文字列を作る O(n²) を、一括の join に変えました。再帰(#6)は、同じ計算の繰り返しをメモ化で消しました。

たとえば#3は、こういう変換でした。

# before: O(n^2) 毎回リストを線形探索
result = []
for order in orders:
    for user in users:
        if user["id"] == order["user_id"]:
            result.append((order, user))

# after: O(n) 辞書で一発引き
user_map = {u["id"]: u for u in users}
result = [(o, user_map[o["user_id"]]) for o in orders]

これは速くなって当然です。探索の回数そのものが減っているからです。AIが本当に効く最適化をするのは、このようにアルゴリズムの計算量を1段下げられるときでした。340msが11msになる(#3)、1,200msが18msになる(#6)ような桁違いの改善は、ここでしか起きませんでした。

逆に言えば、計算量が変わらない書き換えは、速くなる理由がそもそもありません。AIはそこを区別せずに「最適化案」を出してきます。

遅くなった5本に共通すること

問題は遅化した5本です。これも共通点がはっきりしていました。 「速くなりそうな道具」を持ち込んだだけ のケースです。

pandas(#1)もnumpy(#8)も、大量データなら速い道具です。ですが小さいデータでは、ライブラリの読み込みとデータ変換のコストが計算本体を上回ります。0.9msの処理に、配列への変換だけで数msを足したわけです。道具が悪いのではなく、規模に合っていません。

numpy化(#8)はこういう書き換えでした。

# before: 小さいリストの単純な合計
total = sum(x * 2 for x in values)  # values は20要素程度

# after: numpy配列に変換してから計算
import numpy as np
arr = np.array(values)
total = (arr * 2).sum()  # 配列化コストが計算を上回る

20要素のリストに対して、わざわざnumpy配列を作るコストの方が高くつきました。numpyが効くのは数万要素からです。

threadingの並列化(#5)はさらに分かりやすい失敗です。PythonにはGIL(グローバルインタプリタロック)があり、CPUを使う処理ではスレッドを増やしても同時に動けません。1つのスレッドしか実際には計算できないのに、スレッドを作る・切り替えるコストだけが乗ります。並列化どころか、その分だけ遅くなりました。CPUバウンドな処理を本当に並列化したいなら、threading ではなく multiprocessing やプロセスプールが要ります。AIはそこを取り違えていました。

itertools化(#10)は「Pythonicで美しい」コードでした。ですが mapfilter の関数呼び出しの層が増え、素朴な for ループより遅くなりました。Pythonでは関数呼び出し自体にコストがあるからです。美しさと速さは、ここでも別物でした。

AIは「一般に速いとされる手法」を知っています。ですが、目の前のデータ規模・実行環境でそれが速いかどうかは、実際に測らないと分かりません。AIは測らずに「定石」を当ててきます。

意外だったのは「不変」の2本

遅化と同じくらい引っかかったのが、不変だった2本(#7、#9)です。

dataclassへの __slots__ 追加(#7)も、ネストしたifの早期return化(#9)も、よく「速くなる」「綺麗になる」と紹介される手法です。実際、__slots__ はメモリは少し減りました。ですが実行時間はほぼ変わりませんでした。早期returnは可読性は上がりましたが、速度には無関係でした。

ここから分かるのは、 可読性の改善と速度の改善はまったく別 だということです。AIは両方を「最適化」という言葉でまとめて提案してきます。受け取る側が、それは読みやすさの話か、速さの話かを切り分けないと、期待がずれます。早期returnは入れていい。ただし「速くなるから」ではなく「読みやすくなるから」です。

なぜAIは「遅くなる最適化」を出すのか

理由は、AIが学習しているのが「テキストとしての定石」だからだと私は考えています。

「ループより内包表記」「逐次処理より並列化」「自前実装よりライブラリ」。こうした一般論は、世の中の記事に大量に書かれています。AIはそれを正しく再現します。ですが、その定石が成り立つ前提条件(データ規模、CPUバウンドかIOバウンドか)までは、目の前のコードから読み取れていません。

もう1つの理由は、AIが実行環境を持っていないことです。私たちは「測ってから言え」と言えますが、AIは多くの場合コードを実行せずに、見た目から「これは速そう」と判断します。料理の味見をせずにレシピだけで「おいしいはず」と言っているようなものです。だからこそ、実行と計測は人間側が引き受ける必要があります。

2026年に入って、この問題は研究でも正面から扱われ始めました。LLMが生成したコードの実行効率を測るベンチマーク(SWE-fficiency など)や、生成コードの消費電力を測る研究が出てきています。「AIは動くコードを書く」段階は終わり、「そのコードは速いのか・省エネなのか」が次の論点になっています。

実務でどう向き合うか

私の結論は、AIの最適化を信じるな、ではありません。 測ってから採用しろ 、です。

具体的には3つを習慣にしました。

  1. 最適化を頼むときは「定石でなく、このデータ規模で速い案を」と条件を渡す
  2. AIに最適化案と一緒に、計測コード(timeit)も書かせる
  3. before/after を必ず実行し、数字が改善した案だけ採用する

特に2番目が効きました。プロンプトをこう変えただけで、結果が変わります。

# 弱いプロンプト(定石を当ててくる)
このコードを高速化して。

# 強いプロンプト(測らせる)
このコードを高速化して。ただし扱うデータは20〜50件程度。
最適化案と一緒に、before/afterを比較するtimeitの計測コードも書いて。
数字で速くなっていなければ、その案は採用しない。

「データは何件程度か」を渡すと、pandasやnumpyのような大規模向けの道具を、AIが自分で引っ込めることがありました。データ規模はコードからは読み取れないので、人間が教えるしかない情報です。

そして計測コードを一緒に書かせると、AI自身が「思ったより速くなりませんでした」と認めるケースが出てきます。測る習慣を、AIにも持たせるわけです。

もう1つ、計測そのものの注意点があります。1回だけ測ると、ウォームアップやキャッシュの影響でブレます。私が timeit で1,000回まわして中央値を採ったのは、この揺れを潰すためです。1回の実行で「速くなった」と判断すると、誤差を改善と見間違えます。

速くなる/ならないの見分け方

今回の10本から、採用前に効く簡単な目安が見えてきました。

AIの提案 速くなりやすいか
計算量を下げる(O(n²)→O(n)等) 速くなる
メモ化・キャッシュの追加 速くなる
小さいデータへのpandas/numpy導入 遅くなりやすい
CPUバウンド処理のthreading並列化 遅くなりやすい
「Pythonicに」内包表記・itertools化 変わらないか遅くなる
早期return・命名整理 速度は変わらない(可読性は上がる)

「計算量が下がっているか」を1つ確認するだけで、採用すべき提案かどうかの当たりが付きます。下がっていないなら、速くなる理由を疑ってかかる。これだけで判断の精度が上がりました。

エンジニアの仕事は、現状を正しく分析して、正しい方向に努力することだと私は思っています。AIの最適化も同じで、「速くなった気がする」で止めず、数字で現状を確かめる。そこを飛ばすと、半分の確率で逆向きに走ります。

念のため補足すると、これはAIが使えないという話ではありません。#3や#6のように、計算量を下げる最適化はAIの方が速く正確に書けます。私が言いたいのは、AIの提案を「採用するかどうか」の判断は人間が握るべきだ、ということです。提案させるのはAI、採否を決めるのは計測結果。この役割分担にしてから、最適化で逆に遅くするミスがなくなりました。

まとめ

  • AIに最適化させた10本のうち、速くなったのは3本だけ。5本はむしろ遅くなった
  • 速くなったのは計算量(O(n²)→O(n))を下げたケースだけ
  • 遅くなったのは、規模に合わないライブラリ導入・GIL下の並列化・Pythonic化
  • AIは「テキストとしての定石」を当てるが、データ規模や実行環境までは見ていない
  • 対策は1つ。最適化案と計測コードをセットで出させ、数字が改善した案だけ採用する

コードが綺麗に書き換わると、つい速くなった気になります。ですが速さはストップウォッチが決めます。面白くいきましょう。


AIとの協業をコードの速さだけでなく1日の開発フロー全体で設計し直す話は、こちらにまとめています。

Claude Code完全実践ガイド

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?