340 lines
9.3 KiB
PHP
340 lines
9.3 KiB
PHP
<?php
|
|
/**
|
|
* Simple Speed Test Class
|
|
*
|
|
* Just runs queries and times them. No complexity.
|
|
*
|
|
* @package MaplePress_SearchSpeedTest
|
|
*/
|
|
|
|
class MPSS_SpeedTest_Simple {
|
|
|
|
/**
|
|
* Run speed test with specified number of queries.
|
|
*
|
|
* @param int $query_count Number of queries to run.
|
|
* @param string $execution_mode Execution mode: 'serial' or 'parallel'.
|
|
* @return array Results with timing data.
|
|
*/
|
|
public function run( $query_count = 10, $execution_mode = 'serial' ) {
|
|
$start_time = microtime( true );
|
|
|
|
// Debug logging
|
|
error_log( 'MPSS_SpeedTest_Simple::run() called with mode: ' . $execution_mode );
|
|
|
|
// Generate search queries
|
|
$queries = $this->generate_queries( $query_count );
|
|
|
|
// Execute based on mode
|
|
if ( $execution_mode === 'parallel' ) {
|
|
error_log( 'MPSS: Executing in PARALLEL mode' );
|
|
$results = $this->run_parallel( $queries );
|
|
} else {
|
|
error_log( 'MPSS: Executing in SERIAL mode' );
|
|
$results = $this->run_serial( $queries );
|
|
}
|
|
|
|
$end_time = microtime( true );
|
|
$total_time = $end_time - $start_time;
|
|
|
|
// Calculate statistics
|
|
$times = array_column( $results, 'duration_ms' );
|
|
$avg_ms = array_sum( $times ) / count( $times );
|
|
$min_ms = min( $times );
|
|
$max_ms = max( $times );
|
|
|
|
return array(
|
|
'results' => $results,
|
|
'total_time' => round( $total_time, 2 ),
|
|
'total_queries' => count( $results ),
|
|
'avg_ms' => round( $avg_ms, 2 ),
|
|
'min_ms' => round( $min_ms, 2 ),
|
|
'max_ms' => round( $max_ms, 2 ),
|
|
'mode' => $execution_mode,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Run queries in serial mode (one at a time).
|
|
*
|
|
* @param array $queries Array of search query strings.
|
|
* @return array Results array.
|
|
*/
|
|
private function run_serial( $queries ) {
|
|
$results = array();
|
|
|
|
foreach ( $queries as $query ) {
|
|
$query_start = microtime( true );
|
|
|
|
// Make HTTP request to search URL to properly trigger MaplePress hooks
|
|
$search_url = home_url( '/?s=' . urlencode( $query ) );
|
|
$response = wp_remote_get(
|
|
$search_url,
|
|
array(
|
|
'timeout' => 30,
|
|
'headers' => array(
|
|
'X-MaplePress-Test' => '1',
|
|
),
|
|
)
|
|
);
|
|
|
|
$query_end = microtime( true );
|
|
$duration_ms = ( $query_end - $query_start ) * 1000;
|
|
|
|
// Extract result count from response - try multiple patterns
|
|
$result_count = 0;
|
|
if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
|
|
$body = wp_remote_retrieve_body( $response );
|
|
|
|
// Try various patterns to find result count
|
|
$patterns = array(
|
|
'/(\d+)\s+results?/i', // "5 results" or "1 result"
|
|
'/found\s+(\d+)/i', // "Found 5"
|
|
'/showing\s+(\d+)/i', // "Showing 5"
|
|
'/<article/i', // Count article tags
|
|
'/<div[^>]*class="[^"]*post[^"]*"/i', // Count post divs
|
|
);
|
|
|
|
foreach ( $patterns as $pattern ) {
|
|
if ( $pattern === '/<article/i' || strpos( $pattern, '<div' ) === 0 ) {
|
|
// Count HTML elements
|
|
$count = preg_match_all( $pattern, $body );
|
|
if ( $count > 0 ) {
|
|
$result_count = $count;
|
|
break;
|
|
}
|
|
} else {
|
|
// Extract number from text
|
|
if ( preg_match( $pattern, $body, $matches ) ) {
|
|
$result_count = intval( $matches[1] );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$results[] = array(
|
|
'query' => $query,
|
|
'duration_ms' => round( $duration_ms, 2 ),
|
|
'result_count' => $result_count,
|
|
);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Run queries in parallel mode (multiple concurrent requests).
|
|
*
|
|
* @param array $queries Array of search query strings.
|
|
* @return array Results array.
|
|
*/
|
|
private function run_parallel( $queries ) {
|
|
error_log( 'MPSS: run_parallel called with ' . count( $queries ) . ' queries' );
|
|
|
|
$results = array();
|
|
|
|
// Build search URLs
|
|
$search_urls = array();
|
|
foreach ( $queries as $query ) {
|
|
$search_urls[] = array(
|
|
'url' => home_url( '/?s=' . urlencode( $query ) ),
|
|
'query' => $query,
|
|
);
|
|
}
|
|
|
|
error_log( 'MPSS: Built ' . count( $search_urls ) . ' search URLs' );
|
|
|
|
// Initialize cURL multi handle
|
|
$mh = curl_multi_init();
|
|
$curl_handles = array();
|
|
$request_info = array();
|
|
|
|
// Add all requests to multi handle
|
|
foreach ( $search_urls as $index => $search_data ) {
|
|
$ch = curl_init();
|
|
curl_setopt( $ch, CURLOPT_URL, $search_data['url'] );
|
|
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
|
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
|
|
curl_setopt( $ch, CURLOPT_TIMEOUT, 30 );
|
|
curl_setopt( $ch, CURLOPT_HTTPHEADER, array(
|
|
'X-MaplePress-Test: 1',
|
|
) );
|
|
|
|
curl_multi_add_handle( $mh, $ch );
|
|
$ch_id = (int) $ch;
|
|
$curl_handles[ $ch_id ] = $ch;
|
|
$request_info[ $ch_id ] = array(
|
|
'query' => $search_data['query'],
|
|
'index' => $index,
|
|
'start_time' => null,
|
|
'end_time' => null,
|
|
);
|
|
}
|
|
|
|
error_log( 'MPSS: Added ' . count( $curl_handles ) . ' handles, request_info has ' . count( $request_info ) . ' entries' );
|
|
|
|
// Execute all queries simultaneously and track start/end times
|
|
$running = null;
|
|
|
|
// Record start time for ALL requests (they all start together)
|
|
$batch_start = microtime( true );
|
|
foreach ( $request_info as $ch_id => $info ) {
|
|
$request_info[ $ch_id ]['start_time'] = $batch_start;
|
|
}
|
|
|
|
// Start executing - all requests run concurrently
|
|
do {
|
|
curl_multi_exec( $mh, $running );
|
|
|
|
// Check for completed requests and record their end time
|
|
while ( $done = curl_multi_info_read( $mh ) ) {
|
|
if ( $done['msg'] == CURLMSG_DONE ) {
|
|
$ch_id = (int) $done['handle'];
|
|
// Record when THIS specific request finished
|
|
if ( ! isset( $request_info[ $ch_id ]['end_time'] ) ) {
|
|
$request_info[ $ch_id ]['end_time'] = microtime( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( $running > 0 ) {
|
|
curl_multi_select( $mh );
|
|
}
|
|
} while ( $running > 0 );
|
|
|
|
$batch_end = microtime( true );
|
|
$batch_duration_ms = ( $batch_end - $batch_start ) * 1000;
|
|
|
|
error_log( 'MPSS: Batch completed in ' . round( $batch_duration_ms, 2 ) . 'ms, processing ' . count( $request_info ) . ' results' );
|
|
|
|
// Collect results - calculate duration from start/end times
|
|
foreach ( $request_info as $ch_id => $info ) {
|
|
$ch = $curl_handles[ $ch_id ];
|
|
|
|
// Calculate duration from start to end time
|
|
if ( isset( $info['start_time'] ) && isset( $info['end_time'] ) ) {
|
|
$duration_seconds = $info['end_time'] - $info['start_time'];
|
|
$duration_ms = $duration_seconds * 1000;
|
|
|
|
// Debug: log a few samples
|
|
static $debug_count = 0;
|
|
if ( $debug_count < 3 ) {
|
|
error_log( sprintf(
|
|
'MPSS: Sample #%d - start=%.6f, end=%.6f, duration_sec=%.6f, duration_ms=%.2f',
|
|
$debug_count,
|
|
$info['start_time'],
|
|
$info['end_time'],
|
|
$duration_seconds,
|
|
$duration_ms
|
|
) );
|
|
$debug_count++;
|
|
}
|
|
} else {
|
|
// Fallback - shouldn't happen
|
|
$duration_ms = 0;
|
|
}
|
|
|
|
$curl_info = curl_getinfo( $ch );
|
|
$response = curl_multi_getcontent( $ch );
|
|
$http_code = $curl_info['http_code'];
|
|
|
|
// Extract result count using multiple patterns
|
|
$result_count = 0;
|
|
if ( $http_code === 200 && ! empty( $response ) ) {
|
|
$patterns = array(
|
|
'/(\d+)\s+results?/i',
|
|
'/found\s+(\d+)/i',
|
|
'/showing\s+(\d+)/i',
|
|
'/<article/i',
|
|
'/<div[^>]*class="[^"]*post[^"]*"/i',
|
|
);
|
|
|
|
foreach ( $patterns as $pattern ) {
|
|
if ( $pattern === '/<article/i' || strpos( $pattern, '<div' ) === 0 ) {
|
|
$count = preg_match_all( $pattern, $response );
|
|
if ( $count > 0 ) {
|
|
$result_count = $count;
|
|
break;
|
|
}
|
|
} else {
|
|
if ( preg_match( $pattern, $response, $matches ) ) {
|
|
$result_count = intval( $matches[1] );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$results[ $info['index'] ] = array(
|
|
'query' => $info['query'],
|
|
'duration_ms' => round( $duration_ms, 2 ),
|
|
'result_count' => $result_count,
|
|
);
|
|
|
|
curl_multi_remove_handle( $mh, $ch );
|
|
curl_close( $ch );
|
|
}
|
|
|
|
curl_multi_close( $mh );
|
|
|
|
// Sort results by original index to maintain query order
|
|
ksort( $results );
|
|
|
|
return array_values( $results );
|
|
}
|
|
|
|
/**
|
|
* Generate random search queries from existing content.
|
|
*
|
|
* @param int $count Number of queries to generate.
|
|
* @return array Array of search query strings.
|
|
*/
|
|
private function generate_queries( $count ) {
|
|
error_log( 'MPSS: generate_queries called with count=' . $count );
|
|
|
|
$posts = get_posts(
|
|
array(
|
|
'post_type' => array( 'post', 'page' ),
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => $count * 3,
|
|
'orderby' => 'rand',
|
|
)
|
|
);
|
|
|
|
error_log( 'MPSS: get_posts returned ' . count( $posts ) . ' posts' );
|
|
|
|
$queries = array();
|
|
|
|
foreach ( $posts as $post ) {
|
|
if ( count( $queries ) >= $count ) {
|
|
break;
|
|
}
|
|
|
|
// Get words from title
|
|
$words = array_filter( explode( ' ', $post->post_title ) );
|
|
|
|
if ( empty( $words ) ) {
|
|
continue;
|
|
}
|
|
|
|
// 50% single word, 50% two words
|
|
if ( mt_rand( 1, 2 ) === 1 || count( $words ) === 1 ) {
|
|
$queries[] = $words[ array_rand( $words ) ];
|
|
} else {
|
|
$start = mt_rand( 0, count( $words ) - 2 );
|
|
$queries[] = $words[ $start ] . ' ' . $words[ $start + 1 ];
|
|
}
|
|
}
|
|
|
|
// If we don't have enough, pad with generic searches
|
|
while ( count( $queries ) < $count ) {
|
|
$queries[] = 'test';
|
|
}
|
|
|
|
$final_queries = array_slice( $queries, 0, $count );
|
|
error_log( 'MPSS: generate_queries returning ' . count( $final_queries ) . ' queries' );
|
|
|
|
return $final_queries;
|
|
}
|
|
}
|