はじめに
2025年7月、株式会社ジーニーのエンジニア向け 1day インターンに参加しました!
当日は 「広告配信システム(DSP)の高速化」 という実務さながらのテーマに挑戦。
本記事では、私が実装した高速化手法・技術的工夫・学びをまとめます。
高速化施策まとめ
- N+1 問題 → 一括取得化
- キャッシュ導入 → DB アクセス削減
- 並列処理(thread / process) → レスポンス改善
- GIL 対策 → プロセスベース+プリフォーク
🏢 ジーニー社について
- 広告 SaaS を軸に、AI やマーケティング領域へ事業展開
- 生成 AI 検索エンジン「AI SEARCH」を開発
- エンジニアの3割が競技プログラミング出身で実装力が高い
- フルスタック × AI が学べる環境
👉 詳しくは 公式 note をチェック!
インターン概要
項目 | 内容 |
---|---|
開催形式 | オフライン(新宿オークタワー本社) |
使用技術 | Python 3.12 / MySQL / Prometheus / Grafana / Docker |
テーマ | 広告配信処理の高速化チューニング |
社内の雰囲気
開放的な環境でインターンを実施しました!
課題:DSP 広告配信の最適化
処理フロー(要約)
- サイズ一致フィルタ
- ドメインブロック
- カテゴリ制御
- 特徴量ターゲティング
- 価格最大広告の選定
評価条件(例:medium)
項目 | 値 |
---|---|
広告主数 | 300–450 |
リクエスト数 | 100 |
制限時間 | 5 秒 |
実装した高速化施策
1. N+1 問題の解消
# ループ内での fetch → 一括取得に変更
advertisers = fetchAllAdvertisers()
advertiser_map = {adv.id: adv for adv in advertisers}
DB アクセスを O(n) → O(1) に削減!
2. TTL キャッシュの導入
class DataCache:
def __init__(self, ttl_sec: int = 300):
self.cache = {}
self.ttl = ttl_sec
頻繁に変わらないデータは再取得せず即レスポンス!
3. 並列処理(スレッド+条件分岐)
# 特徴量マッチングを並列化
if len(ads_list) > 100:
with ThreadPoolExecutor() as pool:
result = list(pool.map(targeting_func, ads_list))
データサイズに応じて並列化 / 逐次処理を自動切替
4. GIL 回避のためプロセスベースへ
• multiprocessing で真の並列化
• プリフォーク方式で起動時にワーカー生成
• ThreadingHTTPServer → ForkingHTTPServer へ変更
start_time = time.time()
server.prefork_workers() # 事前にワーカーを生成
time.sleep(2)
5. 可視化(Prometheus + Grafana)
curl http://localhost:8888/metrics
• auction_requests_total などのメトリクスを公開
• Grafana ダッシュボードでパフォーマンスを確認!
学び・感想
• 処理順とメトリクス設計が最適化のカギ
• GIL / fork など Python 内部構造の理解がパフォーマンスに直結
• 広告配信の舞台裏で「誰より速く、誰より正確に届ける」面白さを体感
最後に
ジーニーのインターンでは、本番に近い負荷要件下で自分の技術を試せました。
ありがとうございました!