Just before ISUCON10! An example of speeding up processing
Sep 4, 2020
PHP
isucon
high speed
はじめに
高速化の手法の一例を共有する
高速化前のコード
<?php
class Data {
public function get_data_old(){
$pdo = new PDO('mysql:host=localhost;dbname=array_speed_up;','root');
$query = 'SELECT * FROM `data`';
$result = $pdo->query($query)->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
}
function old_code(){
$data = new Data();
$records = $data->get_data_old();
$tmp_array = array();
foreach($records as $record){
array_push($tmp_array, $record['id'].','.'"'.$record['value'].'"');
}
//print_r($tmp_array);
return($tmp_array);
}
echo("--- old ---\n");
$time_start = microtime(true);
$memory_first = memory_get_usage() / (1024 * 1024);
old_code();
$usage_memory = memory_get_usage() / (1024 * 1024);
$memory = $usage_memory - $memory_first;
$memory = round($memory, 4);
$time = microtime(true) - $time_start;
$time = round($time, 4);
echo($memory." MB\n");
echo($time." 秒\n");
DBは以下の構成
CREATE TABLE `data` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`value` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DBからレコードを全て取得し
id,"value"
の形に整形して画面に出力する、というような処理
問題点
まぁ別にレコードが少なければ問題なく動くが全件取得して表示するためレコードが増えれば増えるほど重い
またレコード数分のループを回しているのも重い
$tmp_array
に都度整形した結果を格納していくスタイルなので$records
と$tmp_array
で2倍の配列を確保するのも重い
高速化後のコード
<?php
class Data {
public function get_data_refactor(){
$pdo = new PDO('mysql:host=localhost;dbname=array_speed_up;','root');
$query = 'SELECT CONCAT(CONCAT(CONCAT(CONCAT(`id`, ","), """"), `value`), """") FROM `data`';
$result = $pdo->query($query)->fetchAll(PDO::FETCH_COLUMN);
return $result;
}
}
function refactor_code(){
$data = new Data();
$result = $data->get_data_refactor();
//print_r($result);
return($result);
}
echo("--- refactor ---\n");
$time_start = microtime(true);
$memory_first = memory_get_usage() / (1024 * 1024);
refactor_code();
$usage_memory = memory_get_usage() / (1024 * 1024);
$memory = $usage_memory - $memory_first;
$memory = round($memory, 4);
$time = microtime(true) - $time_start;
$time = round($time, 4);
echo($memory." MB\n");
echo($time." 秒\n");
整形処理をDBから取得するタイミングで既に終わらせているため、データ整形のループが不要
$tmp_array
のような整形後のデータを格納する配列も不要
性能比較
レコード数 | before | after |
---|---|---|
1 | 0.0177 秒 | 0.0014 秒 |
1000 | 0.0295 秒 | 0.0074 秒 |
10000 | 0.1153 秒 | 0.0685 秒 |
100000 | 1.1397 秒 | 0.5736 秒 |
実行時間は大体倍くらいに速くなった。
メモリに関しては0.0002MB
が0.0001MB
に減った。
まとめ
そのループ本当に必要か? その配列本当に必要か? そのテーブルはMAXどの程度レコードが入る想定なのか? 考えうる最善を尽くしていけると良いですね。