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
Ruby on cygwin 2025-02
fd0
0
180
Formの複雑さに立ち向かう
bmthd
1
920
2025.2.14_Developers Summit 2025_登壇資料
0101unite
0
160
Jasprが凄い話
hyshu
0
120
Kubernetes History Inspector(KHI)を触ってみた
bells17
0
250
バッチを作らなきゃとなったときに考えること
irof
2
510
『GO』アプリ バックエンドサーバのコスト削減
mot_techtalk
0
160
Kotlinの開発でも AIをいい感じに使いたい / Making the Most of AI in Kotlin Development
kohii00
4
800
Honoをフロントエンドで使う 3つのやり方
yusukebe
7
3.5k
パスキーのすべて ── 導入・UX設計・実装の紹介 / 20250213 パスキー開発者の集い
kuralab
3
880
5分で理解する SOLID 原則 #phpcon_nagoya
shogogg
1
290
『GO』アプリ データ基盤のログ収集システムコスト削減
mot_techtalk
0
140
Featured
See All Featured
Bash Introduction
62gerente
611
210k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
21
2.5k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
7k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
30
4.6k
KATA
mclloyd
29
14k
4 Signs Your Business is Dying
shpigford
182
22k
Documentation Writing (for coders)
carmenintech
67
4.6k
Scaling GitHub
holman
459
140k
Java REST API Framework Comparison - PWX 2021
mraible
29
8.4k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
7
640
Being A Developer After 40
akosma
89
590k
Designing Experiences People Love
moore
140
23k
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 で質問承ります