創作プラットフォームにおけるコンピュータサイエンス vs 俺たち

公開日:

東京都台東区浅草橋ヒューリックホール&カンファレンスで開催された『traPavilion』でスポンサーセッション(10分)として発表しました。

東京都新宿区株式会社キュービックで開催された『第180回 PHP勉強会@東京』でレギュラーセッション(20分)として発表しました。

Download PDF

スライドテキスト

Page 1

創作プラットフォームにおける
コンピュータサイエンス vs 俺たち

Computer Science vs. Us: An Engineering Story from pixiv

pixiv Inc.
USAMI Kenta

2025-10-13 #traPavilion

東京科学大traP10周年カンファレンス

Page 2

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • 北海道工業大学(2007〜2012年、現北海道科学大学)
  • ピクシブ株式会社 Platform Div > WebTechnology Team PHPer
    • 2012年末から現職、APIとかCIとかいろいろなところを見つめてきました
  • Emacs PHP Modeを開発しています (2017年-)
  • プログラミング言語にこだわりがある (セキュリティ&プログラミング2010)

Page 3

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • 北海道工業大学(2007〜2012年、現北海道科学大学)
  • ピクシブ株式会社 Platform Div > WebTechnology Team PHPer
    • 2012年末から現職、APIとかCIとかいろいろなところを見つめてきました
  • Emacs PHP Modeを開発しています (2017年-)
  • プログラミング言語にこだわりがある (セキュリティ&プログラミング2010)

Page 4

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • 北海道工業大学(2007〜2012年、現北海道科学大学)
  • ピクシブ株式会社 Platform Div > WebTechnology Team PHPer
    • 2012年末から現職、APIとかCIとかいろいろなところを見つめてきました
  • Emacs PHP Modeを開発しています (2017年-)
  • プログラミング言語にこだわりがある (セキュリティ&プログラミング2010)

Page 5

CSっぽいカリキュラム

お前誰よ

全然なくて絶望した

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • 北海道工業大学(2007〜2012年、現北海道科学大学)
  • ピクシブ株式会社 Platform Div > WebTechnology Team PHPer
    • 2012年末から現職、APIとかCIとかいろいろなところを見つめてきました
  • Emacs PHP Modeを開発しています (2017年-)
  • プログラミング言語にこだわりがある (セキュリティ&プログラミング2010)

Page 6

普段やってる仕事

Page 7

<?php

Page 8

Page 9

Page 10

Page 11

pixivとDBを直接 共有しているのは PHP (モノレポ)

Page 12

それ以外は
開発時期や体制に
応じていろいろ

Page 13

Page 14

CPUバウンド
スループット重要

Page 15

CPUバウンド

WebSocket

スループット重要

C10K問題

Page 16

CPUバウンド

WebSocket

スループット重要

C10K問題

普通のWebサービスは
DBのI/Oで律速するので
言語は支配的ではない

Page 17

PHP: Hypertext Preprocessor

  • Web開発にしか使えない、遅い動的言語… と考えられている
  • 型がない、あるいは貧弱な言語… と、認識されている
    • ちょっぴりC言語っぽい標準関数と->
    • そこはかとなくJavaっぽいオブジェクト指向
    • とてもPerlっぽい構文 (特に変数の $ と、文末の ; )

Page 18

こういう言語の
型解析とかFWについて

PHP: Hypertext Preprocessor
発信してます

  • Web開発にしか使えない、遅い動的言語… と考えられている
  • 型がない、あるいは貧弱な言語… と、認識されている
    • ちょっぴりC言語っぽい標準関数と->
    • そこはかとなくJavaっぽいオブジェクト指向
    • とてもPerlっぽい構文 (特に変数の $ と、文末の ; )

Page 19

PHPは見る人の心を映す

Page 20

現役の学生には PHPへの感情は
特にないかも

Page 21

20年以上Webを
支えている言語

Page 22

個人的には
ハックしがいのある
おもしろ言語

Page 23

Page 24

Page 25

pixivは2007年から
絶え間なく
開発が続いている

Page 26

いろんな人たちの
問題解決が
詰まっている

Page 27

さも自分がやった
仕事のように紹介します

Page 28

URL

ルーティング
vs 俺たち

Page 29

Page 30

Page 31

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 32

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 33

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 34

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 35

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 36

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 37

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 38

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 39

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 40

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 41

ファイルをサーバに
コピーすれば
デプロイ完了する

Page 42

Railsとか使えば
綺麗なURLになるよね

Page 43

NovelController#show

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 44

NovelController#show

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 45

NovelController#show

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 46

NovelController#show

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 47

NovelController#show

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 48

NovelController#show

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 49

NovelController#show

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 50

pixivは
フレームワークに
乗ってない

Page 51

時は流れて…

Page 52

pixiv本体にも
URL正規化の流れ

Page 53

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 54

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy' get '/zzz', to: 'zzz'

Page 55

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy'

定数URLは

get '/zzz', to: 'zzz'

ハッシュテーブルで
O(n)で取得

Page 56

# routes.rb
get '/', to: 'index'
get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy'

定数URLは

get '/zzz', to: 'zzz'

ハッシュテーブルで
O(n)で取得

Page 57

# routes.rb

URLにIDが

get '/', to: 'index'

組み込まれている場合は…?

get '/n/:id', to: 'novel#show' get '/u/:id', to: 'user#show'
get '/yyy', to: 'yyy'

定数URLは

get '/zzz', to: 'zzz'

ハッシュテーブルで
O(n)で取得

Page 58

ボトルネックじゃなけりゃ 雑に処理すればいいじゃん
(大富豪の発想)

Page 59

Page 60

URLルーター作った!
(ここまでは自分の仕事)

Page 61

pixiv本体にも
URL正規化の流れ

Page 62

Page 63

たったの100個でこの有様 これだから線形時間はだめ

Page 64

どうにかしよう
(と競プロ得意な同僚が言った)

Page 65

URLルーティングには
実装の定石がある

Page 66

正規表現ベース実装

  • 全部のルーティング対象のパスを結合したパターンを構築して、
    リクエストされたパスをマッチさせる
  • めちゃくちゃ力技っぽく見えるが… アイディアはシンプル
  • ところで… 現代の実用的な正規表現エンジンはJITコンパイルされ、速い

Page 67

基数木(パトリシアトライ)

  • パスを “/" で区切りってネストした連想配列を構築する

Page 68

基数木(パトリシアトライ)

  • パスを “/" で区切りってネストした連想配列を構築する
    get '/', to: 'index'
    get '/xxx', to: 'xxx#index'
    get '/xxx/:id', to: 'xxx#shoq'
    get '/xxx/yyy', to: 'yyy' get '/xxx/zzz’, to: 'zzz'

Page 69

基数木(パトリシアトライ)

  • パスを “/" で区切りってネストした連想配列を構築する

{

get '/', to: 'index'

, : { #result : index }
"" " " " "

get '/xxx', to: 'xxx#index'

xxx , : {
" "

get '/xxx/:id', to: 'xxx#shoq'

: { #result : xxx#index },
"" " " " "

get '/xxx/yyy', to: 'yyy'

:id : { #result : xxx#show },
" " " " " "

get '/xxx/zzz’, to: 'zzz'

yyy : { #result : yyy },
" " " " " "
zzz : { #result : zzz },
" " " " " "
...

Page 70

では作ろう
(さも自分がやったかのように)

Page 71

では作ろう
(さも自分がやったかのように)

Page 72

正規表現

vs
パトリシアトライ

Page 73

Page 74

選ばれたのは
パトリシアトライ
でした

Page 75

パトリシアトライを実装しよう

  • 正規表現に比べアルゴリズムが素朴で、実装もめちゃくちゃ簡単
    • パスのリストから “/” でネストした多次元の連想配列に変換するだけ
    • 可変パラメータは数値と文字列のみ対応 (複雑なパターンも対応は可能)
  • 構築コストはルーティング数によって線形増加するが、十分許容できる
    • キャッシュも可能だが、いまのところはボトルネックになっていない
  • マッチしない場合も処理時間のペナルティがない

Page 76

ルーティングは
できた

Page 77

めでたしめでたし
🎉

Page 78

あとは既存コードを
URLルーターに 載せればいいだけ

Page 79

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 80

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 81

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 82

/data/www/www.pixiv.net (document root)
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php

Page 83

現実にはファイルが 何百個もある!!!

Page 84

/data/www/www.pixiv.net
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php
(実際にはファイルが数百個)

Page 85

/data/www/www.pixiv.net
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php
(実際にはファイルが数百個)

Page 86

/data/www/www.pixiv.net
├── index.php
├── novel
│   └── show.php
├── yyy.php └── zzz.php
(実際にはファイルが数百個)

Page 87

/data/www/www.pixiv.net www.pixiv.net/inc/
├── index.php ├── IndexController.php
├── novel ├── Novel
│   └── show.php │   └── ShowController.php
├── yyy.php ├── YyyController.php └── zzz.php └── ZzzController.php
(実際にはファイルが数百個)

Page 88

手で移植するのは
ナンセンス

Page 89

PHPカンファレンス2017

Page 90

Page 91

Page 92

Page 93

Page 94

Page 95

Page 96

Page 97

今も同じコードが
元気に走り続けてます
🏃

Page 98

枯れた機能なので

積極的に弄る動機がない (PHPのBC breakを踏まない限り)

Page 99

Notes on Programming in C

ルール2: 計測すべし。計測する までは速度のための調整をしては ならない。コードの一部が残りを 圧倒しないのであれば、なおさら
である。

̶ Rob Pike

https://www.lysator.liu.se/c/pikestyle.html

訳語はUNIX哲学 - Wikipediaより引用
(2025年10日2日12:33:03版)

Page 100

裏返せば、計測してみて
スループット悪化の主要因
と判断されない限り
安全性と開発者体験を優先

Page 101

PHPには
型宣言の機能がある

Page 102

処理系が実行時に
自動型チェックを実施

Page 103

実行時?遅いのでは?
🤔

Page 104

かつてはそうだった

Page 105

言語のホットスポットは 処理系で改善されると
嬉しい

Page 106

現代はJITが改善され
型宣言した方が
最適化の恩恵を受ける

Page 107

当てずっぽうで
「速い」ものを選んでも
問題解決にならない

Page 108

これからの時代こそ
自我をもって
問題解決しよう