Python 継続的プロファイリング運用:py-spy/Scalene/OTel Profiling の組み込み方
Python アプリケーションの本番環境での性能問題は、開発時には見えにくいものです。突然のレスポンス低下、メモリリーク、CPU 使用率の急上昇など、ユーザー体験を損なう問題を早期に発見するには、継続的なプロファイリングが欠かせません。
本記事では、本番環境でも安全に利用できる 3 つのプロファイリングツール「py-spy」「Scalene」「OpenTelemetry Profiling」の特徴と、それぞれの組み込み方を詳しく解説します。各ツールの強みを理解し、適切に使い分けることで、パフォーマンスの継続的な可視化と改善が実現できるでしょう。
背景
Python アプリケーションのパフォーマンス管理
Python は開発生産性が高い言語として広く採用されていますが、インタプリタ言語という特性上、パフォーマンスのボトルネックが発生しやすい傾向があります。特に機械学習、データ処理、Web API など、処理負荷の高いアプリケーションでは、どこで時間が消費されているのかを正確に把握することが重要になります。
従来、プロファイリングは開発環境で cProfile や line_profiler を使って実施されてきました。しかし、これらのツールは計測のオーバーヘッドが大きく、本番環境での継続的な利用には向いていませんでした。
継続的プロファイリングの必要性
本番環境でのパフォーマンス問題は、開発環境では再現できないケースが多くあります。実際のデータ量、ユーザーのアクセスパターン、外部サービスとの連携など、本番特有の条件下でのみ発生する問題を検知するには、本番環境でのプロファイリングが不可欠です。
継続的プロファイリングを導入することで、以下のような価値が得られます。
| # | メリット | 詳細 |
|---|---|---|
| 1 | リアルタイム検知 | パフォーマンス劣化を即座に発見 |
| 2 | トレンド分析 | 時系列でのパフォーマンス変化を追跡 |
| 3 | 根本原因の特定 | ボトルネックとなる関数やメモリリークの箇所を特定 |
| 4 | リリース影響の評価 | デプロイ前後での性能比較 |
以下の図は、アプリケーション実行時にプロファイリングツールがどのようにデータを収集し、可視化するかを示しています。
mermaidflowchart TB
app["Python<br/>アプリケーション"]
profiler["プロファイラ"]
collector["データ収集"]
storage["ストレージ"]
viz["可視化<br/>ダッシュボード"]
app -->|実行情報| profiler
profiler -->|サンプリング| collector
collector -->|メトリクス保存| storage
storage -->|データ取得| viz
viz -->|分析・改善| app
プロファイラはアプリケーションの実行情報を低オーバーヘッドで収集し、メトリクスとして蓄積します。蓄積されたデータは可視化ツールで分析され、パフォーマンス改善にフィードバックされるのです。
課題
従来のプロファイリング手法の限界
Python の標準的なプロファイリングツール cProfile は、関数呼び出しのたびに情報を記録するため、実行速度が大幅に低下します。実際の計測では、2〜10 倍程度の速度低下が発生することも珍しくありません。
オーバーヘッドの問題
本番環境で高いオーバーヘッドを伴うプロファイリングを実行すると、ユーザー体験が著しく悪化してしまいます。レスポンスタイムの増加、スループットの低下など、プロファイリング自体がパフォーマンス問題の原因となってしまうのです。
python# cProfile の典型的な利用方法
import cProfile
import pstats
def main():
# アプリケーションのメイン処理
process_data()
# プロファイリング実行(大きなオーバーヘッド)
cProfile.run('main()', 'profile_stats')
上記のコードでは、cProfile.run() がすべての関数呼び出しをトレースするため、処理時間が大幅に増加します。
本番環境での利用困難
cProfile や line_profiler は、アプリケーションコードの変更が必要です。デコレータを追加したり、プロファイリング用のコードを埋め込む必要があるため、本番環境への適用が難しいという課題がありました。
python# line_profiler の利用には@profileデコレータが必要
@profile # 本番コードに残せない
def expensive_function():
result = []
for i in range(10000):
result.append(i ** 2)
return result
デコレータを本番コードに残すと、line_profiler がインストールされていない環境ではエラーが発生します。
可視化と分析の課題
プロファイリング結果を効果的に可視化し、チーム全体で共有することも重要な課題です。従来のツールは、テキストベースの出力が中心で、時系列での比較やトレンド分析が困難でした。
以下の図は、従来のプロファイリング手法が抱える主な課題を示しています。
mermaidflowchart LR
dev["開発環境"]
prod["本番環境"]
dev -->|cProfile実行| overhead["高オーバーヘッド"]
overhead -->|問題| slow["実行速度低下"]
dev -->|コード変更必要| modify["デコレータ追加"]
modify -->|問題| deploy["本番適用困難"]
prod -->|プロファイル実行| impact["ユーザー影響"]
impact -->|問題| ux["UX悪化"]
dev -->|テキスト出力| text["可視化不足"]
text -->|問題| analysis["分析困難"]
このように、オーバーヘッド、本番適用の難しさ、可視化の不足という 3 つの大きな課題が、継続的プロファイリングの導入を妨げてきました。
解決策
最新プロファイリングツールの特徴
本番環境での継続的プロファイリングを実現するため、低オーバーヘッドで動作する新しいツールが登場しています。py-spy、Scalene、OpenTelemetry Profiling は、それぞれ異なるアプローチでこれらの課題を解決しているのです。
py-spy:サンプリングベースの軽量プロファイラ
py-spy は、Rust で実装された外部プロセス型のプロファイラです。Python プロセスに外部からアタッチし、定期的にスタックトレースをサンプリングすることで、オーバーヘッドを 1〜3% 程度に抑えています。
py-spy の主な特徴
| # | 特徴 | 説明 |
|---|---|---|
| 1 | 低オーバーヘッド | サンプリング方式により 1〜3% のオーバーヘッド |
| 2 | コード変更不要 | 外部からプロセスにアタッチ可能 |
| 3 | フレームグラフ出力 | SVG 形式の視覚的な出力 |
| 4 | 本番環境対応 | 実行中のプロセスに影響を与えにくい |
bash# py-spyのインストール
pip install py-spy
# 実行中のPythonプロセスにアタッチ
py-spy record -o profile.svg --pid 12345
この単純なコマンドで、プロセス ID 12345 の Python アプリケーションをプロファイリングし、結果を SVG ファイルに出力できます。
Scalene:詳細なメモリと CPU 分析
Scalene は、CPU 時間だけでなく、メモリ使用量や GPU 使用率まで詳細に分析できる高機能プロファイラです。行単位でのプロファイリングが可能で、どの行がボトルネックになっているかを正確に把握できます。
Scalene の主な特徴
| # | 特徴 | 説明 |
|---|---|---|
| 1 | マルチメトリクス | CPU、メモリ、GPU を同時計測 |
| 2 | 行単位分析 | コードの行ごとに詳細な情報を提供 |
| 3 | メモリリーク検出 | メモリの増減を可視化 |
| 4 | Web UI | ブラウザベースの見やすいインターフェース |
bash# Scaleneのインストール
pip install scalene
# Scaleneでスクリプトを実行
scalene your_script.py
Scalene は実行後、自動的にブラウザで結果を表示し、対話的な分析が可能になります。
OpenTelemetry Profiling:分散システム対応
OpenTelemetry Profiling は、マイクロサービスや分散システムでのプロファイリングに特化したツールです。トレースとプロファイリングを統合し、リクエスト全体のパフォーマンスを可視化できます。
OpenTelemetry Profiling の主な特徴
| # | 特徴 | 説明 |
|---|---|---|
| 1 | 分散トレース統合 | リクエストの全経路をプロファイリング |
| 2 | 標準化されたフォーマット | OTLP プロトコルで統一的にデータ送信 |
| 3 | バックエンド柔軟性 | Jaeger、Grafana など多様なバックエンドに対応 |
| 4 | 継続的収集 | 常時稼働でのプロファイリングが可能 |
以下の図は、3 つのツールの特徴を比較したものです。
mermaidflowchart TB
subgraph pyspy["py-spy"]
ps1["サンプリング方式"]
ps2["CPU プロファイル"]
ps3["フレームグラフ"]
end
subgraph scalene["Scalene"]
sc1["詳細分析"]
sc2["CPU + メモリ + GPU"]
sc3["行単位計測"]
end
subgraph otel["OTel Profiling"]
ot1["分散トレース"]
ot2["標準化プロトコル"]
ot3["継続的収集"]
end
use_case["利用シーン"]
use_case -->|シンプルな<br/>CPU分析| pyspy
use_case -->|詳細な<br/>メモリ調査| scalene
use_case -->|分散システム<br/>全体監視| otel
各ツールは異なる強みを持っており、目的に応じて使い分けることが重要です。
具体例
py-spy の組み込み方
py-spy を本番環境で利用する際の具体的な手順を見ていきましょう。Docker コンテナで動作する Flask アプリケーションを例に説明します。
Flask アプリケーションの準備
まず、プロファイリング対象となる Flask アプリケーションを用意します。
python# app.py - Flaskアプリケーション本体
from flask import Flask, jsonify
import time
app = Flask(__name__)
Flask の基本的なインスタンスを作成します。
python# ルートエンドポイントの定義
@app.route('/api/process')
def process_data():
"""データ処理を行うエンドポイント"""
result = heavy_computation()
return jsonify({'result': result})
このエンドポイントは、後述する重い計算処理を実行します。
python# 重い計算処理をシミュレート
def heavy_computation():
"""CPUを使う処理の例"""
total = 0
for i in range(1000000):
total += i ** 2
time.sleep(0.1) # I/O待機をシミュレート
return total
この関数は CPU 負荷の高い処理と I/O 待機を含み、プロファイリングのテストに適しています。
python# アプリケーションの起動
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
アプリケーションをすべてのネットワークインターフェースで待ち受けるように設定します。
Docker での py-spy 統合
Docker コンテナで py-spy を利用するための Dockerfile を作成します。
dockerfile# Dockerfile - ベースイメージの指定
FROM python:3.11-slim
# 作業ディレクトリの設定
WORKDIR /app
Python 3.11 のスリムイメージを使用し、作業ディレクトリを設定します。
dockerfile# py-spyのインストール
RUN pip install py-spy flask
# アプリケーションファイルのコピー
COPY app.py .
必要なパッケージをインストールし、アプリケーションコードをコピーします。
dockerfile# プロファイリングスクリプトの追加
COPY profiling.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/profiling.sh
後述するプロファイリングスクリプトを追加し、実行権限を付与します。
bash# profiling.sh - プロファイリング実行スクリプト
#!/bin/bash
# Flaskアプリケーションをバックグラウンドで起動
python app.py &
APP_PID=$!
# アプリケーションの起動を待機
sleep 5
Flask アプリケーションをバックグラウンドで起動し、プロセス ID を取得します。
bash# py-spyでプロファイリング開始(60秒間)
py-spy record \
--pid $APP_PID \
--duration 60 \
--rate 100 \
--format flamegraph \
--output /tmp/profile.svg
# アプリケーションを継続実行
wait $APP_PID
py-spy を使って 60 秒間、サンプリングレート 100Hz でプロファイリングを実行します。--format flamegraph オプションでフレームグラフ形式の SVG ファイルが生成されます。
Kubernetes での継続的プロファイリング
Kubernetes 環境で py-spy を継続的に実行するための設定を見ていきます。
yaml# deployment.yaml - Deploymentの基本設定
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
spec:
replicas: 2
selector:
matchLabels:
app: flask-app
2 つのレプリカで Flask アプリケーションをデプロイします。
yaml# Pod仕様とコンテナ設定
template:
metadata:
labels:
app: flask-app
spec:
containers:
- name: app
image: flask-app:latest
ports:
- containerPort: 5000
メインのアプリケーションコンテナを定義します。
yaml# サイドカーコンテナでpy-spyを実行
- name: profiler
image: pyspy:latest
command:
- /bin/sh
- -c
- |
while true; do
py-spy record \
--pid 1 \
--duration 300 \
--rate 100 \
--output /profiles/profile-$(date +%s).svg
sleep 60
done
サイドカーコンテナで 5 分ごとにプロファイリングを実行し、結果をタイムスタンプ付きファイルで保存します。
yaml# 共有ボリュームの設定
volumeMounts:
- name: profiles
mountPath: /profiles
volumes:
- name: profiles
emptyDir: {}
プロファイル結果を保存するための共有ボリュームを設定します。
Scalene の組み込み方
Scalene は開発環境での詳細分析に最適です。データ処理スクリプトを例に使い方を見ていきましょう。
データ処理スクリプトの例
python# data_processor.py - インポート文
import pandas as pd
import numpy as np
from typing import List, Dict
必要なライブラリをインポートします。
python# データ読み込み関数
def load_data(filepath: str) -> pd.DataFrame:
"""CSVファイルからデータを読み込む"""
df = pd.read_csv(filepath)
return df
CSV ファイルを pandas DataFrame として読み込みます。
python# データ変換処理
def transform_data(df: pd.DataFrame) -> pd.DataFrame:
"""データの変換処理を実行"""
# 数値列の正規化
numeric_cols = df.select_dtypes(include=[np.number]).columns
df[numeric_cols] = (df[numeric_cols] - df[numeric_cols].mean()) / df[numeric_cols].std()
# 不要な列を削除
df = df.drop(columns=['temp_col'], errors='ignore')
return df
データの正規化処理を行います。この部分がメモリを多く消費する可能性があります。
python# メモリを多く消費する処理
def aggregate_data(df: pd.DataFrame) -> Dict[str, float]:
"""集計処理を実行"""
results = {}
# グループごとに集計(メモリ負荷が高い)
for group in df['category'].unique():
subset = df[df['category'] == group]
results[group] = subset['value'].sum()
return results
カテゴリごとにデータを抽出して集計します。この処理はメモリ効率が悪い可能性があります。
python# メイン処理
def main():
"""メイン処理フロー"""
df = load_data('large_dataset.csv')
df = transform_data(df)
results = aggregate_data(df)
print(f"処理完了: {len(results)} グループ")
if __name__ == '__main__':
main()
すべての処理を統合して実行します。
Scalene でのプロファイリング実行
bash# Scaleneでプロファイリング実行
scalene --html --outfile profile.html data_processor.py
# CPUとメモリのみを計測する場合
scalene --cpu --memory data_processor.py
--html オプションで HTML 形式のレポートが生成され、ブラウザで詳細な分析が可能になります。
Scalene の出力解析
Scalene は以下のような情報を行単位で提供します。
| # | メトリクス | 説明 |
|---|---|---|
| 1 | CPU 時間 | その行の実行にかかった CPU 時間の割合 |
| 2 | メモリ使用量 | その行で確保されたメモリの量 |
| 3 | メモリ増減 | その行でメモリが増加したか減少したか |
| 4 | GPU 使用率 | GPU を使用している場合の使用率 |
Scalene の出力から、aggregate_data 関数の for ループがメモリを多く消費していることが判明した場合、以下のように改善できます。
python# 改善版:groupbyを使用してメモリ効率を向上
def aggregate_data_optimized(df: pd.DataFrame) -> Dict[str, float]:
"""改善された集計処理"""
# groupbyを使用して効率的に集計
results = df.groupby('category')['value'].sum().to_dict()
return results
pandas の groupby を使うことで、メモリ効率が大幅に向上します。
OpenTelemetry Profiling の組み込み方
OpenTelemetry Profiling を使って、マイクロサービス全体のプロファイリングを実装します。
OpenTelemetry のセットアップ
python# otel_config.py - OpenTelemetryの初期化
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
OpenTelemetry の必要なコンポーネントをインポートします。
python# トレーサープロバイダの設定
def setup_tracing(service_name: str):
"""OpenTelemetryトレーシングの初期化"""
provider = TracerProvider()
trace.set_tracer_provider(provider)
# OTLPエクスポーターの設定
otlp_exporter = OTLPSpanExporter(
endpoint="http://otel-collector:4317",
insecure=True
)
# バッチプロセッサの追加
provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
return trace.get_tracer(service_name)
OTLP プロトコルで OpenTelemetry Collector にデータを送信する設定を行います。
FastAPI アプリケーションへの統合
python# main.py - FastAPIアプリケーション
from fastapi import FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from otel_config import setup_tracing
app = FastAPI()
# OpenTelemetryの初期化
tracer = setup_tracing("user-service")
# FastAPIの自動計装
FastAPIInstrumentor.instrument_app(app)
FastAPI に OpenTelemetry を統合します。FastAPIInstrumentor が自動的にすべてのエンドポイントをトレースします。
python# エンドポイントの定義
@app.get("/api/users/{user_id}")
async def get_user(user_id: int):
"""ユーザー情報を取得"""
with tracer.start_as_current_span("get_user_from_db"):
user = await fetch_user_from_database(user_id)
with tracer.start_as_current_span("enrich_user_data"):
enriched = await enrich_user_data(user)
return enriched
手動でスパンを作成することで、特定の処理単位をプロファイリングできます。
python# データベースアクセス処理
async def fetch_user_from_database(user_id: int):
"""データベースからユーザーを取得"""
# スパンに属性を追加
span = trace.get_current_span()
span.set_attribute("user_id", user_id)
span.set_attribute("db.system", "postgresql")
# データベースクエリの実行
# (実際のDB処理)
return {"id": user_id, "name": "User Name"}
スパンに属性を追加することで、より詳細な情報を記録できます。
Docker Compose でのシステム構成
OpenTelemetry Collector と Jaeger を使った監視環境を構築します。
yaml# docker-compose.yml - サービス定義
version: '3.8'
services:
app:
build: .
ports:
- '8000:8000'
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
depends_on:
- otel-collector
FastAPI アプリケーションを定義し、環境変数で OpenTelemetry Collector のエンドポイントを指定します。
yaml# OpenTelemetry Collectorの設定
otel-collector:
image: otel/opentelemetry-collector:latest
command: ['--config=/etc/otel-collector-config.yaml']
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- '4317:4317' # OTLP gRPC
- '4318:4318' # OTLP HTTP
OpenTelemetry Collector がトレースデータを受信します。
yaml# Jaegerバックエンドの設定
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- '16686:16686' # Jaeger UI
- '14250:14250' # gRPC
environment:
- COLLECTOR_OTLP_ENABLED=true
Jaeger でトレースデータを可視化します。
OpenTelemetry Collector の設定
yaml# otel-collector-config.yaml - レシーバーの設定
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
OTLP プロトコルでデータを受信する設定を行います。
yaml# プロセッサの設定
processors:
batch:
timeout: 10s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
バッチ処理とメモリ制限を設定し、Collector の安定性を確保します。
yaml# エクスポーターの設定
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
logging:
loglevel: debug
Jaeger にデータを送信する設定と、デバッグ用のログ出力を設定します。
yaml# パイプラインの定義
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [jaeger, logging]
受信したトレースデータを処理し、Jaeger に送信するパイプラインを定義します。
以下の図は、OpenTelemetry を使った監視システム全体の流れを示しています。
mermaidflowchart LR
app["FastAPI<br/>アプリケーション"]
otel["OTel<br/>Collector"]
jaeger["Jaeger<br/>バックエンド"]
ui["Jaeger UI"]
app -->|"OTLP(gRPC)"| otel
otel -->|"トレース<br/>データ"| jaeger
jaeger -->|"可視化"| ui
ui -->|"分析結果"| dev["開発者"]
dev -->|"改善"| app
アプリケーションから Jaeger UI まで、データが一貫したフォーマットで流れることで、マイクロサービス全体のパフォーマンスを統合的に監視できます。
3 つのツールの使い分け
実際のプロジェクトでは、これらのツールを組み合わせて使用することが効果的です。
| # | ツール | 利用シーン | 環境 |
|---|---|---|---|
| 1 | py-spy | CPU ボトルネックの特定 | 本番環境 |
| 2 | Scalene | メモリリークの調査 | 開発・ステージング環境 |
| 3 | OTel Profiling | マイクロサービス全体の監視 | 本番環境 |
| 4 | py-spy + OTel | 分散システムでの詳細分析 | 本番環境 |
たとえば、本番環境で CPU 使用率が高いことを OTel Profiling で検知した場合、該当サービスに py-spy をアタッチして詳細なフレームグラフを取得します。メモリリークが疑われる場合は、ステージング環境で同じ負荷をかけながら Scalene で行単位の分析を実施するといった使い分けが有効です。
まとめ
Python アプリケーションの継続的プロファイリングは、本番環境でのパフォーマンス管理に欠かせない技術となっています。従来のプロファイリングツールが持つ高いオーバーヘッドや本番適用の難しさという課題は、py-spy、Scalene、OpenTelemetry Profiling といった最新ツールによって解決されつつあります。
py-spy は低オーバーヘッドでシンプルな CPU プロファイリングを実現し、本番環境での継続的な監視に最適です。Scalene は CPU、メモリ、GPU を詳細に分析でき、開発段階でのパフォーマンス最適化に強力な威力を発揮します。OpenTelemetry Profiling は分散システム全体を統合的に監視し、マイクロサービスアーキテクチャでのボトルネック特定を可能にします。
これらのツールを適切に使い分け、継続的にプロファイリングデータを収集・分析することで、パフォーマンス問題の早期発見と迅速な改善が実現できるでしょう。プロファイリングを開発フローに組み込み、データドリブンなパフォーマンス改善を進めていくことが、高品質な Python アプリケーションを提供する鍵となります。
まずは開発環境で Scalene を試し、ステージング環境で py-spy を実行し、最終的に本番環境で OpenTelemetry Profiling を導入するという段階的なアプローチをお勧めします。各ツールの特性を理解し、チームの開発フローに合わせて最適な組み合わせを見つけていってください。
関連リンク
articlePython 継続的プロファイリング運用:py-spy/Scalene/OTel Profiling の組み込み方
articlePython ドメインイベント設計:Outbox・整合性・再試行をコードで実装
articleRuby と Python を徹底比較:スクリプト・Web・データ処理での得意分野
articlePython subprocess チート:標準入出力・パイプ・非同期実行の最短レシピ
articlePython Dev Containers 完全レシピ:再現可能な開発箱を VS Code で作る
articlePython ORMs 実力検証:SQLAlchemy vs Tortoise vs Beanie の選び方
articleWebSocket Close コード早見表:正常終了・プロトコル違反・ポリシー違反の実務対応
articleStorybook 品質ゲート運用:Lighthouse/A11y/ビジュアル差分を PR で自動承認
articleWebRTC で高精細 1080p/4K 画面共有:contentHint「detail」と DPI 最適化
articleSolidJS フォーム設計の最適解:コントロール vs アンコントロールドの棲み分け
articleWebLLM 使い方入門:チャット UI を 100 行で実装するハンズオン
articleShell Script と Ansible/Make/Taskfile の比較:小規模自動化の最適解を検証
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来