хуйу нас не матерятся
В этот раз посмотрим стандартную во всех языках функцию замены подстроки в строке. В php это str_replace, в javaScript str.replace, в python str.replace, и в lua str:gsub.
Почему именно эту функцию? А потому, что практически в любом веб-проекте используется какой-либо шаблонизатор, а уже шаблонизатор по сути занимается тем что в шаблоне (текстовая переменная) меняет определённые подстроки (ключи) на нужные данные. В простейшим виде, в шаблон вида:
<html>
<body>
<h1>{title}</h1>
<div class="page-text">{text}</div>
</body>
</hrml>
Вместо меток {title} и {text}, подставляются данные из какой-либо СУБД(например mysql как в 90% субд на просторах интернетов). Конечно есть ещё способ не делать string replace, а исполнять код внутри шаблона, например так:
<?php
// $title = ...
// $text = ...
?>
<html>
<body>
<h1><?php echo $title ?></h1>
<div class="page-text"><?php echo $text ?></div>
</body>
</hrml>
И он будет работать на много быстрее шаблонизаторов. Но в современном веб-деве принято жестко отделять всю логику от шаблонов.
<?php
function getRandomChar() {
$chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
return $chars{ rand(0, strlen($chars) - 1 ) };
}
function createString($length) {
$half = (int) $length / 2;
$result = '';
for($i=0; $i<$half; $i++ ) {
$result .= getRandomChar();
}
$result .= '{REPLACE_ME}';
for($i=0; $i<$half; $i++ ) {
$result .= getRandomChar();
}
return $result;
}
function test($strLength, $count) {
$startTime = microtime(true);
echo "str_length=$strLength test_count=$count, start_time=$startTime";
$soutseString = createString($strLength);
for($i=0; $i<$count; $i++)
$res = str_replace('{REPLACE_ME}', '~TEST~', $soutseString);
$worktime = microtime(true) - $startTime;
echo " RESULT=$worktime
";
}
echo " ~ TEST ~
";
test(1000, 100000000);
test(10000, 10000000);
test(100000, 1000000);
?>
from random import randrange
import time
def getRandomChar():
chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
return chars[ randrange(0, len(chars) - 1) ]
def createString(length):
result = ''
half = (int)( length / 2 )
i = 0
while i < half:
result += getRandomChar()
i += 1
result += '{REPLACE_ME}'
i = 0
while i < half:
result += getRandomChar()
i += 1
return result
def test(strLength, count):
startTime = time.time()
print( "str_length=" + str(strLength) + ", test_count=" + str(count) + ", start_time=" + str(startTime) )
soutseString = createString(strLength)
i = 0
while i < count:
i += 1
res = soutseString.replace("{REPLACE_ME}", "~TEST~")
worktime = time.time() - startTime
print( "RESULT=" + str(worktime) )
test(1000, 100000000)
test(10000, 10000000)
test(100000, 1000000)
function getRandomChar() {
let chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
return chars[ Math.floor(Math.random() * (chars.length-1)) ];
}
function createString(length) {
let half = parseInt( length / 2 );
let result = '';
for(let i=0; i<half; i++ ) {
result += getRandomChar();
}
result += '{REPLACE_ME}';
for(let i=0; i<half; i++ ) {
result += getRandomChar();
}
return result;
}
function test(strLength, count) {
let startTime = Date.now();
console.log( `str_length=${strLength}, test_count=${count}, start_time= ${startTime}`);
let soutseString = createString(strLength);
let rep = new RegExp('{REPLACE_ME}', 'g');
for(i=0; i<count; i++) {
let res = soutseString.replace(rep, '~TEST~');
}
let worktime = Date.now() - startTime;
console.log( `RESULT=${worktime / 1000}`);
}
console.log( ' ~TEST~' );
test(1000, 100000000);
test(10000, 10000000);
test(100000, 1000000);
function getRandomChar()
local chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
local symbIndex = math.random(1,chars:len() )
return chars:sub(symbIndex, symbIndex)
end
function createString(length)
local half = math.floor(length / 2)
local result = ''
for i = 1, half, 1 do
result = result .. getRandomChar()
end
result = result .. '{REPLACE_ME}'
for i = 1, half, 1 do
result = result .. getRandomChar()
end
return result
end
function test(strLength, count)
local startTime = os.clock();
print( 'str_length=' .. strLength .. ', test_count=' .. count .. ', start_time=' .. startTime);
soutseString = createString(strLength)
for i = 1, count, 1 do
local res = soutseString:gsub('{REPLACE_ME}', '~TEST~')
end
local workTime = os.clock() - startTime;
print( 'RESULT=' .. workTime )
end
math.randomseed( os.time() )
test(1000, 100000000)
test(10000, 10000000)
test(100000, 1000000)
Для тестов как обычно используем 5-и долларовую виртуалку на линоде, версии софта: PHP: 7.0.30, Python: 3.5.3, Node.js: 10.6.0, Lua: 5.3.3, LuaJit: 2.0.4. За исключением ноды, весь софт из стандартного дистрибутива debian. Нода в стандартном дистре очень древняя, но у неё есть собственный репозиторий для всех видов линюксов, в отличии, например, от пхп.
str_length*test_count | 1000*100000000 | 10000*10000000 | 100000*1000000 |
---|---|---|---|
Node.js | 17.969s | 4.498s | 3.541s |
PHP | 20.4376s | 92.5527s | 87.6090s |
Python | 62.6877s | 23.4150s | 33.5308s |
LuaJIT | 1275.6611s | 1233.8939s | 1266.4118s |
Lua | 1304.2875s | 1276.2183s | 1272.9350s |
Значения указаны в секундах. Чем значение меньше, тем быстрее отработал скрипт, тем лучше.
Я не смог дождаться результатов от луа, скрипт после 15 минут так и не выдал даже первые данные, по этому, для луа, я уменьшил в 10 раз количество итераций, а результаты соответственно умножил на 10. Не помогла и Jit компиляция, именно в этом случае она вообще не дала никакого прироста в скорости.
Ну и нода , как обычно рулит, пхп и питон +- одинаковы, каждый в своей категории.