Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions benchmark.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

function benchmark_strtoupper(string $str) {
$start = microtime(true);
for ($i = 0; $i < 100000; $i++) {
strtoupper($str);
}
$end = microtime(true);
return $end - $start;
}

$short_ascii = 'abcdefghijklmnopqrstuvwxyz';
$long_ascii = str_repeat($short_ascii, 1000);
$short_mixed = 'aBcDeFgHiJkLmNoPqRsTuVwXyZ';
$long_mixed = str_repeat($short_mixed, 1000);
$short_upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$long_upper = str_repeat($short_upper, 1000);

echo "Benchmarking current strtoupper implementation...\n";

echo "Short ASCII string: " . benchmark_strtoupper($short_ascii) . "s\n";
echo "Long ASCII string: " . benchmark_strtoupper($long_ascii) . "s\n";
echo "Short mixed-case string: " . benchmark_strtoupper($short_mixed) . "s\n";
echo "Long mixed-case string: " . benchmark_strtoupper($long_mixed) . "s\n";
echo "Short uppercase string: " . benchmark_strtoupper($short_upper) . "s\n";
echo "Long uppercase string: " . benchmark_strtoupper($long_upper) . "s\n";
38 changes: 38 additions & 0 deletions benchmark_array_combine.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

function benchmark($name, $func) {
$start = microtime(true);
// Run the function multiple times to get a more stable reading
for ($i = 0; $i < 10; $i++) {
$func();
}
$end = microtime(true);
printf("%-40s: %8.4f seconds\n", $name, ($end - $start));
}

const LARGE_SIZE = 1000000;

// --- Scenario 1: Packed Indexed Arrays (Target for optimization) ---
$indexed_keys = range(0, LARGE_SIZE - 1);
$indexed_values = range(0, LARGE_SIZE - 1);

benchmark("Packed Indexed Arrays", function() use ($indexed_keys, $indexed_values) {
$a = array_combine($indexed_keys, $indexed_values);
});


// --- Scenario 2: Associative Arrays (Check for regressions) ---
$assoc_keys = [];
$assoc_values = [];
for ($i = 0; $i < LARGE_SIZE; $i++) {
$assoc_keys[] = "key_" . $i;
$assoc_values[] = $i;
}

benchmark("Associative Arrays", function() use ($assoc_keys, $assoc_values) {
$a = array_combine($assoc_keys, $assoc_values);
});

echo "\n";

?>
48 changes: 48 additions & 0 deletions benchmark_grapheme_str_split.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

function benchmark($name, $func) {
// A relatively low iteration count because grapheme operations can be intensive.
$iterations = 500;
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$func();
}
$end = microtime(true);
echo str_pad($name, 45) . ": " . number_format($end - $start, 6) . "s\n";
}

echo "Benchmarking current grapheme_str_split implementation...\n";

// A long string (~50KB) mixing ASCII, multi-byte, and complex graphemes.
$long_string = str_repeat("Hello world! Это тест. The quick brown 👨‍👩‍👧‍👦 fox. Á, B́, Ć.", 200);

// ===== TEST CASES =====

// Case 1: Simple ASCII string
benchmark("Simple ASCII string", function() {
grapheme_str_split("abcdefghijklmnopqrstuvwxyz");
});

// Case 2: Multi-byte UTF-8 string (Cyrillic)
benchmark("Multi-byte UTF-8 string (Cyrillic)", function() {
grapheme_str_split("абвгдеёжзийклмнопрстуфхцчшщъыьэюя");
});

// Case 3: Complex Graphemes (Combining Marks)
// 'e' with 3 combining marks is one grapheme
benchmark("Complex Graphemes (Combining Marks)", function() {
grapheme_str_split("é̄̃");
});

// Case 4: Complex Graphemes (Emoji)
// Family emoji and woman with skin tone modifier are single graphemes
benchmark("Complex Graphemes (Emoji)", function() {
grapheme_str_split("👨‍👩‍👧‍👦👩🏽‍💻");
});

// Case 5: Long mixed string
benchmark("Long mixed string", function() use ($long_string) {
grapheme_str_split($long_string);
});

?>
44 changes: 44 additions & 0 deletions benchmark_range.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

function benchmark($name, $func) {
$start = microtime(true);
$func();
$end = microtime(true);
printf("%-40s: %8.4f seconds\n", $name, $end - $start);
}

const LARGE_INT = 10000000;
const LARGE_FLOAT = 10000000.0;

// --- Integer Benchmarks ---
benchmark("Integer Range (1 to 10,000,000)", function() {
$a = range(1, LARGE_INT);
});

benchmark("Integer Range (10,000,000 to 1)", function() {
$a = range(LARGE_INT, 1);
});

benchmark("Integer Range with Step (1 to 10,000,000, step 2)", function() {
$a = range(1, LARGE_INT, 2);
});


// --- Float Benchmarks ---
benchmark("Float Range (1.0 to 10,000,000.0)", function() {
$a = range(1.0, LARGE_FLOAT);
});

benchmark("Float Range with Step (10,000,000.0 to 1.0, step 2.5)", function() {
$a = range(LARGE_FLOAT, 1.0, 2.5);
});


// --- Character Benchmark ---
benchmark("Character Range ('a' to 'z')", function() {
$a = range('a', 'z');
});

echo "\n";

?>
51 changes: 51 additions & 0 deletions benchmark_str_decrement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

function benchmark($name, $func) {
$start = microtime(true);
// Using a smaller iteration count because string operations can be slow
for ($i = 0; $i < 20000; $i++) {
$func();
}
$end = microtime(true);
echo "$name: " . ($end - $start) . "s\n";
}

echo "Benchmarking current str_decrement implementation...\n";

// Case 1: Simple numeric string, no borrow needed.
$s1 = "123456789123456789";
benchmark("Simple numeric string", function() use ($s1) {
str_decrement($s1);
});

// Case 2: Long numeric string with full borrow. This is the key case for my optimization.
$s2 = "1" . str_repeat("0", 50);
benchmark("Long numeric string with full borrow", function() use ($s2) {
str_decrement($s2);
});

// Case 3: Simple alphanumeric string, no borrow needed.
$s3 = "abcdefg9";
benchmark("Simple alphanumeric string", function() use ($s3) {
str_decrement($s3);
});

// Case 4: Long alphanumeric string with numeric borrow.
$s4 = "b" . str_repeat("0", 50);
benchmark("Long alphanumeric with numeric borrow", function() use ($s4) {
str_decrement($s4);
});

// Case 5: Long alphanumeric string with letter borrow.
$s5 = "z" . str_repeat("a", 50);
benchmark("Long alphanumeric with letter borrow", function() use ($s5) {
str_decrement($s5);
});

// Case 6: A long string that does not trigger the leading-zero optimization.
$s6 = "2" . str_repeat("0", 50);
benchmark("Long numeric string without leading-zero result", function() use ($s6) {
str_decrement($s6);
});

?>
49 changes: 49 additions & 0 deletions benchmark_str_ends_with.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

function benchmark($name, $func) {
$start = microtime(true);
// This function is very fast, so a high iteration count is needed.
for ($i = 0; $i < 500000; $i++) {
$func();
}
$end = microtime(true);
echo str_pad($name, 40) . ": " . number_format($end - $start, 6) . "s\n";
}

echo "Benchmarking current str_ends_with implementation...\n";

$long_haystack = "This is a very long string that is used for benchmarking purposes to see how the function performs with a significant amount of data to process.";
$long_needle_match = "a significant amount of data to process.";
$long_needle_no_match = "a significant amount of data to process!";

// Case 1: Haystack shorter than needle (fast path)
benchmark("Haystack shorter than needle", function() {
str_ends_with("short", "this is much longer");
});

// Case 2: Matching short needle
benchmark("Matching short needle", function() use ($long_haystack) {
str_ends_with($long_haystack, "process.");
});

// Case 3: Non-matching short needle
benchmark("Non-matching short needle", function() use ($long_haystack) {
str_ends_with($long_haystack, "process!");
});

// Case 4: Matching long needle
benchmark("Matching long needle", function() use ($long_haystack, $long_needle_match) {
str_ends_with($long_haystack, $long_needle_match);
});

// Case 5: Non-matching long needle
benchmark("Non-matching long needle", function() use ($long_haystack, $long_needle_no_match) {
str_ends_with($long_haystack, $long_needle_no_match);
});

// Case 6: Matching empty needle (edge case)
benchmark("Matching empty needle", function() use ($long_haystack) {
str_ends_with($long_haystack, "");
});

?>
50 changes: 50 additions & 0 deletions benchmark_str_increment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

function benchmark($name, $func) {
$start = microtime(true);
for ($i = 0; $i < 20000; $i++) {
$func();
}
$end = microtime(true);
echo "$name: " . ($end - $start) . "s\n";
}

echo "Benchmarking current str_increment implementation...\n";

// Case 1: Simple numeric string, no carry needed.
$s1 = "123456788";
benchmark("Simple numeric string", function() use ($s1) {
str_increment($s1);
});

// Case 2: Long numeric string with full carry. This is the key case for my optimization.
$s2 = str_repeat("9", 50);
benchmark("Long numeric string with full carry", function() use ($s2) {
str_increment($s2);
});

// Case 3: Simple alphanumeric string, no carry needed.
$s3 = "abcde8";
benchmark("Simple alphanumeric string", function() use ($s3) {
str_increment($s3);
});

// Case 4: Long alphanumeric string with numeric carry.
$s4 = "a" . str_repeat("9", 50);
benchmark("Long alphanumeric with numeric carry", function() use ($s4) {
str_increment($s4);
});

// Case 5: Long alphanumeric string with full letter and numeric carry.
$s5 = "z" . str_repeat("9", 50);
benchmark("Long alphanumeric with full carry", function() use ($s5) {
str_increment($s5);
});

// Case 6: A long string that does not trigger reallocation.
$s6 = "8" . str_repeat("9", 50);
benchmark("Long numeric string without reallocation", function() use ($s6) {
str_increment($s6);
});

?>
47 changes: 47 additions & 0 deletions benchmark_str_ireplace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

function benchmark($name, $func) {
$start = microtime(true);
// A lower iteration count because str_ireplace can be very slow with arrays on long strings.
for ($i = 0; $i < 500; $i++) {
$func();
}
$end = microtime(true);
echo str_pad($name, 50) . ": " . number_format($end - $start, 6) . "s\n";
}

echo "Benchmarking current str_ireplace implementation...\n";

// A long string (~22KB) with a variety of characters to search for.
$long_haystack = str_repeat("The quick brown fox jumps over the lazy dog. ", 500);

// An array of search terms that will all be found.
$search_array = ['the', 'quick', 'brown', 'fox', 'jumps', 'over', 'lazy', 'dog'];
$replace_array = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];

// An array of search terms that will not be found.
$search_array_no_match = ['xylophone', 'yak', 'zebra', 'walrus', 'vulture', 'unicorn', 'tiger', 'snake'];

// ===== TEST CASES =====

// Case 1: Simple string search (no arrays) for baseline comparison.
benchmark("Simple string search", function() use ($long_haystack) {
str_ireplace('fox', 'cat', $long_haystack);
});

// Case 2: Array search, single replace. THIS IS THE PRIMARY OPTIMIZATION TARGET.
benchmark("Array search, single replacement", function() use ($long_haystack, $search_array) {
str_ireplace($search_array, 'REPLACED', $long_haystack);
});

// Case 3: Array search, array replace. THIS IS THE PRIMARY OPTIMIZATION TARGET.
benchmark("Array search, array replacement", function() use ($long_haystack, $search_array, $replace_array) {
str_ireplace($search_array, $replace_array, $long_haystack);
});

// Case 4: Array search with no matches. To ensure no performance regression.
benchmark("Array search, no matches", function() use ($long_haystack, $search_array_no_match) {
str_ireplace($search_array_no_match, 'REPLACED', $long_haystack);
});

?>
27 changes: 27 additions & 0 deletions benchmark_str_pad.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

function benchmark_str_pad(string $input, int $pad_length, string $pad_string, int $pad_type) {
$start = microtime(true);
for ($i = 0; $i < 100000; $i++) {
str_pad($input, $pad_length, $pad_string, $pad_type);
}
$end = microtime(true);
return $end - $start;
}

$short_string = 'Hello';
$long_string = str_repeat('Hello', 100);

echo "Benchmarking current str_pad implementation...\n";

echo "Short string, right pad: " . benchmark_str_pad($short_string, 100, ' ', STR_PAD_RIGHT) . "s\n";
echo "Short string, left pad: " . benchmark_str_pad($short_string, 100, ' ', STR_PAD_LEFT) . "s\n";
echo "Short string, both pad: " . benchmark_str_pad($short_string, 100, ' ', STR_PAD_BOTH) . "s\n";

echo "Long string, right pad: " . benchmark_str_pad($long_string, 1000, ' ', STR_PAD_RIGHT) . "s\n";
echo "Long string, left pad: " . benchmark_str_pad($long_string, 1000, ' ', STR_PAD_LEFT) . "s\n";
echo "Long string, both pad: " . benchmark_str_pad($long_string, 1000, ' ', STR_PAD_BOTH) . "s\n";

echo "Short string, multi-char pad, right: " . benchmark_str_pad($short_string, 100, '-=', STR_PAD_RIGHT) . "s\n";
echo "Short string, multi-char pad, left: " . benchmark_str_pad($short_string, 100, '-=', STR_PAD_LEFT) . "s\n";
echo "Short string, multi-char pad, both: " . benchmark_str_pad($short_string, 100, '-=', STR_PAD_BOTH) . "s\n";
Loading
Loading