Slide 1

Slide 1 text

自作ArrayでPHPのメモリ節約 2015年11月23日(火)   “よや”  [email protected]

Slide 2

Slide 2 text

自己紹介 •  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  

Slide 3

Slide 3 text

【問題提起】  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  だと  

Slide 4

Slide 4 text

何故 PHP  array は重たいのか  h8ps://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view

Slide 5

Slide 5 text

もう少し分かりやすい図 ・つまりDataコンテナもArray管理データも大きい h8ps://catchy.io/slides/PHP-­‐Data-­‐Structures-­‐and-­‐the-­‐impact-­‐of-­‐PHP-­‐7-­‐on-­‐them.html

Slide 6

Slide 6 text

SplFixedArray の方は? ・Array管理データがごそっと減ってる h8ps://catchy.io/slides/PHP-­‐Data-­‐Structures-­‐and-­‐the-­‐impact-­‐of-­‐PHP-­‐7-­‐on-­‐them.html テーブル長固定   双方向リスト   構造無し

Slide 7

Slide 7 text

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  

Slide 8

Slide 8 text

【提案】もっと節約したい •  使える値をUint8  (0〜255)に絞る (制約)   – メモリに等間隔で詰めて並べられるので有利   – Stringに保存する事で管理も殆どしない   Uint8Array String   (5文字)   0   1   2   3   $a  =  Uint8Array(5)   $a[1]  =  7;   var_dump($a[3]);   4  

Slide 9

Slide 9 text

array自体をPHPで実装出来る •  配列風  class  を実現する以下の機能がある   – ArrayAccess   •  $a[〜]  =  7  のようにアクセスできる   – Countable   •  count($a)  で配列数がわかる   – Iterator   •  foreach($a  as  $e)  でループ出来る class  MyArray  implements  ArrayAccess,                                                                                                    Countable,                                                                                                        Iterator  {  

Slide 10

Slide 10 text

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)  

Slide 11

Slide 11 text

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)  

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

注意点3つ 自作Arrayの注意点 (SplFixedArray  とほぼ共通)     •  (1)  array_〜  系関数が使えない   •  array_slice  や array_join  が(そのままだと)使えない   •  (2)  is_array  に引っかからない   •  is_object  なので   •  (3)要素を再帰的に持つ場合に注意   •  SplFixedArray  だと要素を参照渡しできない   •  今回の自作Arrayだとそもそも再帰的に持てない  

Slide 15

Slide 15 text

(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;   }   作ればいい

Slide 16

Slide 16 text

(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)  

Slide 17

Slide 17 text

(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  だと   エラー

Slide 18

Slide 18 text

Array_Typed •  h8ps://github.com/yoya/Array_Typed   – 使い方サンプル

Slide 19

Slide 19 text

ご利用上の注意 •  利用出来る値の範囲は限られます   –  Uint8:  0〜255、Sint8:  -­‐128〜127   –  Uint16:  0〜65535、Sint16:  -­‐32768〜32767   •  圧倒的な省メモリ、だけど CPU  は数倍重たい   •  以下は SplFixedArrayと共通のTips   –  配列の長さは固定です。あとで増減出来ません   –  Array_〜関数を使うときは toArray()  で変換して渡す     –  再帰構造の途中には使わない。リーフ限定にする   –  is_array  の代わりに instanceof  でチェック

Slide 20

Slide 20 text

備考 •  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

Slide 21

Slide 21 text

    以上です     twi8er_id:yoya で質問承ります