Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
array_typed
Search
yoya
November 23, 2015
Programming
0
2.2k
array_typed
自作Arrayでメモリ節約
yoya
November 23, 2015
Tweet
Share
More Decks by yoya
See All by yoya
resize_nitpick
yoya
1
150
ImageFluxBinary
yoya
2
2.8k
HEIF-kaisetsu
yoya
4
3.3k
go-thumber-imagick
yoya
1
180
chokaizomae
yoya
2
570
wildimagebinary
yoya
1
220
goimagicksyokai
yoya
2
1.1k
GoImagickThumbnail
yoya
0
1.6k
sushigazou
yoya
0
12k
Other Decks in Programming
See All in Programming
A Gopher's Guide to Vibe Coding
danicat
0
200
【第4回】関東Kaggler会「Kaggleは執筆に役立つ」
mipypf
0
990
Laravel Boost 超入門
fire_arlo
2
170
レガシープロジェクトで最大限AIの恩恵を受けられるようClaude Codeを利用する
tk1351
4
1.5k
Claude Codeで実装以外の開発フロー、どこまで自動化できるか?失敗と成功
ndadayo
3
1.8k
サーバーサイドのビルド時間87倍高速化
plaidtech
PRO
0
690
時間軸から考えるTerraformを使う理由と留意点
fufuhu
7
2.1k
Zendeskのチケットを Amazon Bedrockで 解析した
ryokosuge
3
230
私の後悔をAWS DMSで解決した話
hiramax
4
180
TROCCO×dbtで実現する人にもAIにもやさしいデータ基盤
nealle
0
390
速いWebフレームワークを作る
yusukebe
3
1.2k
ライブ配信サービスの インフラのジレンマ -マルチクラウドに至ったワケ-
mirrativ
2
270
Featured
See All Featured
Fireside Chat
paigeccino
39
3.6k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.5k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
51
5.6k
Designing for Performance
lara
610
69k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
Code Review Best Practice
trishagee
70
19k
Faster Mobile Websites
deanohume
309
31k
Facilitating Awesome Meetings
lara
55
6.5k
Why Our Code Smells
bkeepers
PRO
339
57k
Scaling GitHub
holman
463
140k
Automating Front-end Workflow
addyosmani
1370
200k
GitHub's CSS Performance
jonrohan
1032
460k
Transcript
自作ArrayでPHPのメモリ節約 2015年11月23日(火) “よや” yoya@awm.jp
自己紹介 • h8ps://osdn.jp/users/yoya/ • h8ps://github.com/yoya/ • 元Flashバイナリ屋
– h8ps://osdn.jp/projects/swfed/ – モバイルFlashが終わって仕事がなくなりました • ImageMagick マニア – 差分追ってます – h8p://d.hatena.ne.jp/yoya/searchdiary? word=ImageMagick twi8er_id:yoya
【問題提起】 PHPの配列は重たい $size = 20 * 1024*1024; // 20M
$arr = array(); for ($i = 0 ; $i < $size; $i++) { $arr[$i] = $i; } $total = 0; for ($i = 0 ; $i < $size; $i++) { $total += $arr[$i]; } • CPU: user 0m12.174s sys 0m2.568s • Memory: 2.9GB • CPU: user 0m0.384s sys 0m0.132s Memory: 157MB PHPの 現実 size := 20 * 1000 * 1000; arr := make([]int, size) for i := 0; i < size; i++ { arr[i] = i } total := 0 for i := 0; i < size; i++ { total += arr[i] } ちなみに Golang だと
何故 PHP array は重たいのか h8ps://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view
もう少し分かりやすい図 ・つまりDataコンテナもArray管理データも大きい h8ps://catchy.io/slides/PHP-‐Data-‐Structures-‐and-‐the-‐impact-‐of-‐PHP-‐7-‐on-‐them.html
SplFixedArray の方は? ・Array管理データがごそっと減ってる h8ps://catchy.io/slides/PHP-‐Data-‐Structures-‐and-‐the-‐impact-‐of-‐PHP-‐7-‐on-‐them.html テーブル長固定 双方向リスト 構造無し
SplFixedArray で1/3に $size = 20 * 1024*1024; // 20M
$arr = array(); for ($i = 0 ; $i < $size; $i++) { $arr[$i] = $i; } $total = 0; for ($i = 0 ; $i < $size; $i++) { $total += $arr[$i]; } • CPU: user 0m12.174s sys 0m2.568s • Memory: 2.9GB 普通の Array $size = 20 * 1024*1024; // 20M $arr = new SplFixedArray($size); for ($i = 0 ; $i < $size; $i++) { <あとは同じ> • CPU: user 0m3.779s sys 0m0.637s • Memory: 1.0GB SplFixedArray
【提案】もっと節約したい • 使える値をUint8 (0〜255)に絞る (制約) – メモリに等間隔で詰めて並べられるので有利 – Stringに保存する事で管理も殆どしない
Uint8Array String (5文字) 0 1 2 3 $a = Uint8Array(5) $a[1] = 7; var_dump($a[3]); 4
array自体をPHPで実装出来る • 配列風 class を実現する以下の機能がある – ArrayAccess • $a[〜]
= 7 のようにアクセスできる – Countable • count($a) で配列数がわかる – Iterator • foreach($a as $e) でループ出来る class MyArray implements ArrayAccess, Countable, Iterator {
AccessArray • get,set,isset,unset を実装 class Uint8Array implements ArrayAccess {
protected $container; funcmon __construct($n) { $this-‐>arraySize = $n; $this-‐>container = str_repeat("\0", $n); } public funcmon offsetGet($offset) { return ord($this-‐>container[$offset]); } public funcmon offsetSet($offset, $value) { $this-‐>container[$offset] = chr($value); } public funcmon offsetExists($offset) { ;;; } public funcmon offsetUnset($offset) { ;;; } } $a = new Uint8Array(5); $a[2] = 7; var_dump($a[1], $a[2]); => int(0) int(7)
Countable • count を実装 class Uint8Array implements ArrayAccess,
Countable { protected $container; funcmon __construct($n) { $this-‐>arraySize = $n; $this-‐>container = str_repeat("\0", $n); } <略> public funcmon count() { return $this-‐>arraySize; } } $a = new Uint8Array(5); var_dump(count($a)); => int(5)
Iterator • current,next,key,valid,rewind を実装 class Uint8Array implements ArrayAccess,
Countable, Iterator { <略> public funcmon current() { return $this-‐>offsetGet($this-‐>iterPos); } public funcmon next() { $this-‐>iterPos++; } public funcmon key() { return $this-‐>iterPos; } public funcmon valid() { return $this-‐>iterPos < $this-‐>arraySize; } public funcmon rewind() { $this-‐>iterPos = 0; } } $a = new Uint8Array(5); $a[2] = 7; foreach ($a as $e) { var_dump($e); } => int(0) int(0) int(7) int(0) int(0) Iterator の cursor
Uint8Array でメモリ大幅節約 $size = 20 * 1024*1024; // 20M
$arr = array(); for ($i = 0 ; $i < $size; $i++) { $arr[$i] = $i % 256; } $total = 0; for ($i = 0 ; $i < $size; $i++) { $total += $arr[$i]; } • CPU: user 0m12.174s sys 0m2.568s • Memory: 2.9GB 普通の Array $size = 20 * 1024*1024; // 20M $arr = new Uint8Array($size); for ($i = 0 ; $i < $size; $i++) { <あとは同じ> • CPU: user 0m27.828s sys 0m0.121s • Memory: 20MB Uint8Array メモリ使用量 100分の1
注意点3つ 自作Arrayの注意点 (SplFixedArray とほぼ共通) • (1) array_〜 系関数が使えない
• array_slice や array_join が(そのままだと)使えない • (2) is_array に引っかからない • is_object なので • (3)要素を再帰的に持つ場合に注意 • SplFixedArray だと要素を参照渡しできない • 今回の自作Arrayだとそもそも再帰的に持てない
(1) array_〜系関数が使えない • toArray で標準arrayに変換してから渡す – SplFixedArray は toArray メソッドがある
• array_slice は真面目に作る必要がありそう • 一部切り出すのにtoArrayで展開するとメモリが辛い funcmon toArray() { $arr = array(); $arraySize = $this-‐>arraySize; for ($i = 0 ; $i < $arraySize ; $i++) { $arr []= $this-‐>offsetGet($i); } return $arr; } 作ればいい
(2) is_array にひっかからない • ArrayAccess 実装は is_array 対象ではない require_once("Uint8Array.php");
$a1 = array(); $a3 = new SplFixedArray(3); $a3 = new Uint8Array(3); echo “# is_array¥n”; var_dump(is_array($a1)); var_dump(is_array($a2)); var_dump(is_array($a3)); echo “# instanceof¥n”; var_dump($a2 instanceof SplFixedArray); var_dump($a3 instanceof Uint8Array); var_dump($a2 instanceof ArrayAccess); var_dump($a3 instanceof ArrayAccess); # array bool(true) bool(false) bool(false) # Instanceof bool(true) bool(true) bool(true) bool(true)
(3)要素を参照渡しできない ※SplFixedArray の話、蛇足 • 構造を再帰的に持つときに致命的 $arr = [1, 2,
[31,32,33], 4]; print_r($arr); add($arr); print_r($arr); funcmon add(&$arr) { if (is_array($arr)) { foreach ($arr as &$e) { add($e); } } else { $arr += 1; } } 1 2 * 4 31 32 33 PHP Nomce: Indirect modificamon of overloaded element of SplFixedArray has no effect in /home/yoya/ array_ref.php on line 8 全部1足す 2 3 * 5 32 33 34 SplFixedArray だと エラー
Array_Typed • h8ps://github.com/yoya/Array_Typed – 使い方サンプル <?php require 'vendor/autoload.php';
$arr = new Array_Uint8(1024); $arr[100] = 0x7F; var_dump($arr[100]); $arr = new Array_Sint8(1024); $arr[100] = -‐0x80; var_dump($arr[100]); Array_{Uint8,Sint8,Uint16,Sint16} の4つをご用意しました int(127) int(-‐128)
ご利用上の注意 • 利用出来る値の範囲は限られます – Uint8: 0〜255、Sint8: -‐128〜127 –
Uint16: 0〜65535、Sint16: -‐32768〜32767 • 圧倒的な省メモリ、だけど CPU は数倍重たい • 以下は SplFixedArrayと共通のTips – 配列の長さは固定です。あとで増減出来ません – Array_〜関数を使うときは toArray() で変換して渡す – 再帰構造の途中には使わない。リーフ限定にする – is_array の代わりに instanceof でチェック
備考 • PHP7 だと Array が大分軽い – 冒頭の 20*1024*1024
配列を読み書きするコード • PHP拡張にすればCPUとメモリの両方に優しい仕 組みが作れるはず > 若いものに任せます、作り方なら教えるYO! PHP5 PHP7 array 12.2sec, 2.9GB 1.7sec, 1.0GB SplFixedArray 3.8sec, 1.0GB 1.6sec, 320MB Uint8Array 27.8sec, 20MB 15.2sec, 20MB
以上です twi8er_id:yoya で質問承ります