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.1k
array_typed
自作Arrayでメモリ節約
yoya
November 23, 2015
Tweet
Share
More Decks by yoya
See All by yoya
resize_nitpick
yoya
1
130
ImageFluxBinary
yoya
2
2.4k
HEIF-kaisetsu
yoya
4
3k
go-thumber-imagick
yoya
1
150
chokaizomae
yoya
2
470
wildimagebinary
yoya
1
190
goimagicksyokai
yoya
2
980
GoImagickThumbnail
yoya
0
1.3k
sushigazou
yoya
0
11k
Other Decks in Programming
See All in Programming
エンターテイメント業界で利用されるAWS
demuyan
0
200
Netty Chicago Java User Group 2024-04-17
sullis
0
130
二郎系ラーメンのコールで学ぶ AST 解析
memory1994
PRO
7
1.7k
CircleCIを活用して AWSへの継続的デリバリーを 実践する
coconala_engineer
1
230
Code Reviews
bkuhlmann
4
880
Ruby Pattern Matching
bkuhlmann
0
920
Milestoner
bkuhlmann
1
400
Ruby GitHub Packages
bkuhlmann
0
620
CQRS/ES avec Symfony, c’est (trop) bien !
jeremyfreeagent
1
630
PHP8.3の機能を振り返る / Review of PHP 8.3 features
seike460
PRO
1
110
本格ローグライク制作にEbitengineを選んでみた
nagainaganawa
0
290
ログラスを支える設計標準について / loglass-design-standards
urmot
10
2.1k
Featured
See All Featured
No one is an island. Learnings from fostering a developers community.
thoeni
14
2.1k
Unsuck your backbone
ammeep
662
57k
Web Components: a chance to create the future
zenorocha
305
41k
Large-scale JavaScript Application Architecture
addyosmani
503
110k
Product Roadmaps are Hard
iamctodd
43
9.7k
Build The Right Thing And Hit Your Dates
maggiecrowley
23
2k
Documentation Writing (for coders)
carmenintech
59
3.9k
Raft: Consensus for Rubyists
vanstee
132
6.2k
The Language of Interfaces
destraynor
151
23k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
9
8.3k
Git: the NoSQL Database
bkeepers
PRO
422
63k
Making the Leap to Tech Lead
cromwellryan
123
8.5k
Transcript
自作ArrayでPHPのメモリ節約 2015年11月23日(火) “よや”
[email protected]
自己紹介 • 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 で質問承ります