PHPでプログラムの実行時間測定やパフォーマンス計測を行う際に欠かせないmicrotime
関数について、基本的な使い方から実際の活用例まで詳しく解説します。マイクロ秒レベルの精密な時間計測をマスターして、効率的なパフォーマンス分析を実現しましょう。
microtime関数とは?
microtime
(micro time)は、現在のUnixタイムスタンプをマイクロ秒(100万分の1秒)の精度で取得するPHPの関数です。通常のtime()
関数が秒単位の精度なのに対し、microtime
はより高精度な時間測定が可能で、プログラムの実行時間計測やパフォーマンス分析において重要な役割を果たします。
基本構文
microtime(bool $as_float = false): string|float
パラメータ:
$as_float
: 戻り値の形式を指定false
(デフォルト): 文字列形式 “マイクロ秒部分 秒部分”true
: 浮動小数点数形式
戻り値:
false
の場合:"0.12345600 1679123456"
のような文字列true
の場合:1679123456.123456
のような浮動小数点数
基本的な使用例
1. 文字列形式での取得
// 文字列形式で取得
$microtime_str = microtime();
echo $microtime_str; // 例: "0.12345600 1679123456"
// 文字列を分解して使用
list($microsec, $sec) = explode(' ', microtime());
echo "秒: " . $sec . "\n";
echo "マイクロ秒部分: " . $microsec . "\n";
echo "合計: " . ($sec + $microsec) . "\n";
2. 浮動小数点数形式での取得(推奨)
// 浮動小数点数形式で取得(より扱いやすい)
$microtime_float = microtime(true);
echo $microtime_float; // 例: 1679123456.123456
// 日付形式で表示
echo date('Y-m-d H:i:s', $microtime_float) . '.' .
sprintf('%06d', ($microtime_float - floor($microtime_float)) * 1000000);
// 出力例: 2024-03-15 14:30:56.123456
実践的な活用例
1. 実行時間測定システム
class PerformanceProfiler
{
private $startTimes = [];
private $measurements = [];
/**
* 測定開始
*/
public function start($label = 'default')
{
$this->startTimes[$label] = microtime(true);
}
/**
* 測定終了と結果取得
*/
public function end($label = 'default')
{
if (!isset($this->startTimes[$label])) {
throw new InvalidArgumentException("測定ラベル '{$label}' が見つかりません");
}
$endTime = microtime(true);
$executionTime = $endTime - $this->startTimes[$label];
$this->measurements[$label] = [
'start_time' => $this->startTimes[$label],
'end_time' => $endTime,
'execution_time' => $executionTime,
'formatted_time' => $this->formatTime($executionTime)
];
unset($this->startTimes[$label]);
return $executionTime;
}
/**
* 時間の整形表示
*/
private function formatTime($seconds)
{
if ($seconds < 0.001) {
return number_format($seconds * 1000000, 2) . ' μs';
} elseif ($seconds < 1) {
return number_format($seconds * 1000, 2) . ' ms';
} else {
return number_format($seconds, 3) . ' s';
}
}
/**
* 全測定結果の表示
*/
public function getReport()
{
$report = "=== パフォーマンス測定結果 ===\n";
foreach ($this->measurements as $label => $data) {
$report .= sprintf(
"%-20s: %s\n",
$label,
$data['formatted_time']
);
}
return $report;
}
/**
* 最も時間のかかった処理を特定
*/
public function getSlowestProcess()
{
if (empty($this->measurements)) {
return null;
}
$slowest = array_reduce($this->measurements, function($carry, $item) {
return (!$carry || $item['execution_time'] > $carry['execution_time'])
? $item : $carry;
});
return $slowest;
}
}
// 使用例
$profiler = new PerformanceProfiler();
// データベース処理の測定
$profiler->start('database');
// 重い処理をシミュレート
usleep(50000); // 50ミリ秒待機
$profiler->end('database');
// ファイル処理の測定
$profiler->start('file_processing');
for ($i = 0; $i < 1000000; $i++) {
$dummy = $i * $i;
}
$profiler->end('file_processing');
// 結果表示
echo $profiler->getReport();
echo "\n最も時間のかかった処理:\n";
$slowest = $profiler->getSlowestProcess();
echo $slowest ? $profiler->formatTime($slowest['execution_time']) : 'なし';
2. APIレスポンス時間監視システム
class APIMonitor
{
private $responseLog = [];
/**
* API呼び出し時間測定
*/
public function measureAPICall($url, $method = 'GET', $data = null)
{
$startTime = microtime(true);
try {
// cURLでAPI呼び出し
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => $data ? json_encode($data) : null,
CURLOPT_HTTPHEADER => $data ? ['Content-Type: application/json'] : []
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$endTime = microtime(true);
$responseTime = $endTime - $startTime;
// 結果をログに保存
$this->logResponse($url, $method, $responseTime, $httpCode, !empty($error));
return [
'success' => empty($error),
'response_time' => $responseTime,
'http_code' => $httpCode,
'response' => $response,
'error' => $error
];
} catch (Exception $e) {
$endTime = microtime(true);
$responseTime = $endTime - $startTime;
$this->logResponse($url, $method, $responseTime, 0, true);
return [
'success' => false,
'response_time' => $responseTime,
'error' => $e->getMessage()
];
}
}
/**
* レスポンス情報をログに記録
*/
private function logResponse($url, $method, $responseTime, $httpCode, $hasError)
{
$this->responseLog[] = [
'timestamp' => microtime(true),
'url' => $url,
'method' => $method,
'response_time' => $responseTime,
'http_code' => $httpCode,
'has_error' => $hasError,
'formatted_time' => $this->formatResponseTime($responseTime)
];
}
/**
* レスポンス時間の統計情報取得
*/
public function getStatistics()
{
if (empty($this->responseLog)) {
return null;
}
$times = array_column($this->responseLog, 'response_time');
$successCount = count(array_filter($this->responseLog, function($log) {
return !$log['has_error'] && $log['http_code'] < 400;
}));
return [
'total_requests' => count($this->responseLog),
'success_requests' => $successCount,
'success_rate' => ($successCount / count($this->responseLog)) * 100,
'average_time' => array_sum($times) / count($times),
'min_time' => min($times),
'max_time' => max($times),
'median_time' => $this->calculateMedian($times)
];
}
private function calculateMedian($values)
{
sort($values);
$count = count($values);
if ($count % 2 === 0) {
return ($values[$count/2 - 1] + $values[$count/2]) / 2;
} else {
return $values[($count - 1) / 2];
}
}
private function formatResponseTime($seconds)
{
if ($seconds < 1) {
return number_format($seconds * 1000, 1) . 'ms';
} else {
return number_format($seconds, 2) . 's';
}
}
}
// 使用例
$monitor = new APIMonitor();
// 複数のAPI呼び出しを測定
$apis = [
'https://api.example1.com/users',
'https://api.example2.com/products',
'https://api.example3.com/orders'
];
foreach ($apis as $api) {
$result = $monitor->measureAPICall($api);
echo "{$api}: {$result['formatted_time']}\n";
}
// 統計情報表示
$stats = $monitor->getStatistics();
if ($stats) {
echo "\n=== API統計情報 ===\n";
echo "総リクエスト数: {$stats['total_requests']}\n";
echo "成功率: " . number_format($stats['success_rate'], 1) . "%\n";
echo "平均レスポンス時間: " . number_format($stats['average_time'] * 1000, 1) . "ms\n";
echo "最速: " . number_format($stats['min_time'] * 1000, 1) . "ms\n";
echo "最遅: " . number_format($stats['max_time'] * 1000, 1) . "ms\n";
}
3. データベースクエリ最適化支援システム
class DatabaseProfiler
{
private $queryLog = [];
private $currentQuery = null;
/**
* クエリ実行前の準備
*/
public function beforeQuery($sql, $params = [])
{
$this->currentQuery = [
'sql' => $sql,
'params' => $params,
'start_time' => microtime(true),
'memory_start' => memory_get_usage()
];
}
/**
* クエリ実行後の処理
*/
public function afterQuery($result = null, $error = null)
{
if (!$this->currentQuery) {
return;
}
$endTime = microtime(true);
$executionTime = $endTime - $this->currentQuery['start_time'];
$memoryUsage = memory_get_usage() - $this->currentQuery['memory_start'];
$this->queryLog[] = [
'sql' => $this->currentQuery['sql'],
'params' => $this->currentQuery['params'],
'execution_time' => $executionTime,
'memory_usage' => $memoryUsage,
'success' => $error === null,
'error' => $error,
'timestamp' => $this->currentQuery['start_time']
];
$this->currentQuery = null;
}
/**
* 遅いクエリの特定
*/
public function getSlowQueries($threshold = 0.1)
{
return array_filter($this->queryLog, function($query) use ($threshold) {
return $query['execution_time'] > $threshold;
});
}
/**
* クエリ統計レポート
*/
public function generateReport()
{
if (empty($this->queryLog)) {
return "クエリログがありません。";
}
$totalQueries = count($this->queryLog);
$successfulQueries = count(array_filter($this->queryLog, function($q) {
return $q['success'];
}));
$executionTimes = array_column($this->queryLog, 'execution_time');
$totalTime = array_sum($executionTimes);
$averageTime = $totalTime / $totalQueries;
$slowQueries = $this->getSlowQueries(0.1);
$report = "=== データベースクエリ分析レポート ===\n\n";
$report .= "総クエリ数: {$totalQueries}\n";
$report .= "成功率: " . number_format(($successfulQueries / $totalQueries) * 100, 1) . "%\n";
$report .= "総実行時間: " . number_format($totalTime * 1000, 2) . "ms\n";
$report .= "平均実行時間: " . number_format($averageTime * 1000, 2) . "ms\n";
$report .= "遅いクエリ数 (>100ms): " . count($slowQueries) . "\n\n";
if (!empty($slowQueries)) {
$report .= "=== 遅いクエリ詳細 ===\n";
foreach ($slowQueries as $index => $query) {
$report .= sprintf(
"%d. 実行時間: %sms\n SQL: %s\n\n",
$index + 1,
number_format($query['execution_time'] * 1000, 2),
$query['sql']
);
}
}
return $report;
}
}
// 使用例(PDOとの組み合わせ)
$profiler = new DatabaseProfiler();
try {
$pdo = new PDO('sqlite::memory:');
// テーブル作成
$profiler->beforeQuery('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
$profiler->afterQuery();
// データ挿入(遅い処理をシミュレート)
for ($i = 1; $i <= 100; $i++) {
$sql = 'INSERT INTO users (name) VALUES (?)';
$profiler->beforeQuery($sql, ["User{$i}"]);
$stmt = $pdo->prepare($sql);
$stmt->execute(["User{$i}"]);
// わざと遅延を追加
if ($i % 10 === 0) {
usleep(150000); // 150ms
}
$profiler->afterQuery();
}
// レポート出力
echo $profiler->generateReport();
} catch (Exception $e) {
$profiler->afterQuery(null, $e->getMessage());
echo "エラー: " . $e->getMessage();
}
4. 簡単な実行時間測定関数
/**
* 関数やコードブロックの実行時間を測定
*/
function measureTime($callback, $label = 'execution')
{
$startTime = microtime(true);
$result = $callback();
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
$formattedTime = $executionTime < 0.001
? number_format($executionTime * 1000000, 2) . ' μs'
: ($executionTime < 1
? number_format($executionTime * 1000, 2) . ' ms'
: number_format($executionTime, 3) . ' s');
echo "{$label}: {$formattedTime}\n";
return [
'result' => $result,
'execution_time' => $executionTime,
'formatted_time' => $formattedTime
];
}
// 使用例
$measurement = measureTime(function() {
// 重い処理のシミュレート
$sum = 0;
for ($i = 0; $i < 1000000; $i++) {
$sum += $i;
}
return $sum;
}, 'ループ処理');
echo "結果: " . $measurement['result'] . "\n";
高精度時間計測のベストプラクティス
1. 複数回測定による精度向上
function accurateMeasurement($callback, $iterations = 100)
{
$times = [];
for ($i = 0; $i < $iterations; $i++) {
$start = microtime(true);
$callback();
$end = microtime(true);
$times[] = $end - $start;
}
sort($times);
$count = count($times);
return [
'min' => min($times),
'max' => max($times),
'average' => array_sum($times) / $count,
'median' => $count % 2 === 0
? ($times[$count/2 - 1] + $times[$count/2]) / 2
: $times[($count - 1) / 2],
'iterations' => $iterations
];
}
// 使用例
$results = accurateMeasurement(function() {
array_sum(range(1, 1000));
}, 1000);
echo "平均実行時間: " . number_format($results['average'] * 1000000, 2) . " μs\n";
echo "中央値: " . number_format($results['median'] * 1000000, 2) . " μs\n";
2. メモリ使用量との組み合わせ測定
class ResourceProfiler
{
public static function profile($callback, $label = 'process')
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
$startPeakMemory = memory_get_peak_usage();
$result = $callback();
$endTime = microtime(true);
$endMemory = memory_get_usage();
$endPeakMemory = memory_get_peak_usage();
return [
'label' => $label,
'result' => $result,
'execution_time' => $endTime - $startTime,
'memory_used' => $endMemory - $startMemory,
'peak_memory_used' => $endPeakMemory - $startPeakMemory,
'formatted' => [
'time' => number_format(($endTime - $startTime) * 1000, 2) . 'ms',
'memory' => self::formatBytes($endMemory - $startMemory),
'peak_memory' => self::formatBytes($endPeakMemory - $startPeakMemory)
]
];
}
private static function formatBytes($size)
{
$units = ['B', 'KB', 'MB', 'GB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
}
// 使用例
$profile = ResourceProfiler::profile(function() {
$array = range(1, 100000);
return array_sum($array);
}, '大きな配列の処理');
echo "処理: {$profile['label']}\n";
echo "実行時間: {$profile['formatted']['time']}\n";
echo "メモリ使用量: {$profile['formatted']['memory']}\n";
echo "ピークメモリ: {$profile['formatted']['peak_memory']}\n";
注意点とベストプラクティス
1. 精度に関する注意
// システムの時計精度を確認
echo "システムの時計精度テスト:\n";
for ($i = 0; $i < 5; $i++) {
$time1 = microtime(true);
$time2 = microtime(true);
echo "連続取得の差: " . number_format(($time2 - $time1) * 1000000, 2) . " μs\n";
}
2. パフォーマンス測定時の考慮事項
// 測定精度を上げるための準備
function prepareMeasurement()
{
// ガベージコレクションを実行
if (function_exists('gc_collect_cycles')) {
gc_collect_cycles();
}
// CPUキャッシュをウォームアップ
for ($i = 0; $i < 100; $i++) {
microtime(true);
}
echo "測定準備完了\n";
}
まとめ
microtime
関数は、PHPにおける高精度な時間測定とパフォーマンス分析の核となる重要な関数です。マイクロ秒レベルの精密な測定により、プログラムの最適化や問題の特定を効率的に行うことができます。
重要なポイント:
microtime(true)
の浮動小数点形式が扱いやすく推奨- 複数回測定による精度向上を心がける
- メモリ使用量など他の指標との組み合わせが効果的
- システムの制約や環境による精度の違いを理解
- 本番環境での測定は最小限に抑制
これらの知識と実践例を活用することで、より効率的で高性能なPHPアプリケーションの開発が可能になるでしょう。