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
140
ImageFluxBinary
yoya
2
2.6k
HEIF-kaisetsu
yoya
4
3.2k
go-thumber-imagick
yoya
1
160
chokaizomae
yoya
2
520
wildimagebinary
yoya
1
200
goimagicksyokai
yoya
2
1.1k
GoImagickThumbnail
yoya
0
1.5k
sushigazou
yoya
0
12k
Other Decks in Programming
See All in Programming
たのしいSocketのしくみ / Socket Under a Microscope
coe401_
8
1.3k
color-scheme: light dark; を完全に理解する
uhyo
7
490
もう少しテストを書きたいんじゃ〜 #phpstudy
o0h
PRO
17
4k
『テスト書いた方が開発が早いじゃん』を解き明かす #phpcon_nagoya
o0h
PRO
8
2.4k
はじめての Go * WASM *OCR
sgash708
1
100
Datadog DBMでなにができる? JDDUG Meetup#7
nealle
0
140
Domain-Driven Transformation
hschwentner
2
1.9k
PRレビューのお供にDanger
stoticdev
1
230
技術を改善し続ける
gumioji
0
120
Ça bouge du côté des animations CSS !
goetter
2
150
sappoRo.R #12 初心者セッション
kosugitti
0
280
kintone開発を効率化するためにチームで試した施策とその結果を大放出!
oguemon
0
150
Featured
See All Featured
Bash Introduction
62gerente
611
210k
Java REST API Framework Comparison - PWX 2021
mraible
29
8.4k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
29
1k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
49k
Building Applications with DynamoDB
mza
93
6.2k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
Designing for humans not robots
tammielis
250
25k
Unsuck your backbone
ammeep
669
57k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.7k
Code Review Best Practice
trishagee
67
18k
Documentation Writing (for coders)
carmenintech
67
4.6k
Six Lessons from altMBA
skipperchong
27
3.6k
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 で質問承ります