Upgrade to Pro — share decks privately, control downloads, hide ads and more …

array_typed

yoya
November 23, 2015

 array_typed

自作Arrayでメモリ節約

yoya

November 23, 2015
Tweet

More Decks by yoya

Other Decks in Programming

Transcript

  1. 自己紹介 •  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  
  2. 【問題提起】  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  だと  
  3. 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  
  4. array自体をPHPで実装出来る •  配列風  class  を実現する以下の機能がある   – ArrayAccess   •  $a[〜]

     =  7  のようにアクセスできる   – Countable   •  count($a)  で配列数がわかる   – Iterator   •  foreach($a  as  $e)  でループ出来る class  MyArray  implements  ArrayAccess,                                                                                                    Countable,                                                                                                        Iterator  {  
  5. 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)  
  6. 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)  
  7. 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
  8. 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
  9. 注意点3つ 自作Arrayの注意点 (SplFixedArray  とほぼ共通)     •  (1)  array_〜  系関数が使えない

      •  array_slice  や array_join  が(そのままだと)使えない   •  (2)  is_array  に引っかからない   •  is_object  なので   •  (3)要素を再帰的に持つ場合に注意   •  SplFixedArray  だと要素を参照渡しできない   •  今回の自作Arrayだとそもそも再帰的に持てない  
  10. (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;   }   作ればいい
  11. (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)  
  12. (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  だと   エラー
  13. 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)  
  14. ご利用上の注意 •  利用出来る値の範囲は限られます   –  Uint8:  0〜255、Sint8:  -­‐128〜127   – 

    Uint16:  0〜65535、Sint16:  -­‐32768〜32767   •  圧倒的な省メモリ、だけど CPU  は数倍重たい   •  以下は SplFixedArrayと共通のTips   –  配列の長さは固定です。あとで増減出来ません   –  Array_〜関数を使うときは toArray()  で変換して渡す     –  再帰構造の途中には使わない。リーフ限定にする   –  is_array  の代わりに instanceof  でチェック
  15. 備考 •  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