isset () vs strlen () - быстрый / чистый расчет длины строки - PullRequest
55 голосов
/ 05 августа 2011

Я сталкивался с этим кодом ...

if(isset($string[255])) {
    // too long
}

isset () на 6-40 быстрее, чем

if(strlen($string) > 255) {
    // too long
}

Единственным недостатком isset () является то, чтоКод неясен - мы не можем сразу сказать, что делается (см. ответ Пекки).Мы можем обернуть isset () внутри функции, то есть strlt ($ string, 255), но тогда мы потеряем преимущества скорости от isset ().

Как мы можем использовать более быструю функцию isset (), сохраняячитаемость кода?

EDIT: тест, чтобы показать скорость http://codepad.org/ztYF0bE3

strlen() over 1000000 iterations 7.5193998813629
isset() over 1000000 iterations 0.29940009117126

EDIT2: вот почему isset () быстрее

$string = 'abcdefg';
var_dump($string[2]);
Output: string(1) “c”

$string = 'abcdefg';
if (isset($string[7])){
     echo $string[7].' found!';
  }else{
     echo 'No character found at position 7!';
}

Это быстрее, чем использование strlen (), потому что «… вызов функции обходится дороже, чем использование языковой конструкции». http://www.phpreferencebook.com/tips/use-isset-instead-of-strlen/

EDIT3: меня всегда учили интересоваться мирко-оптимизацией.Вероятно потому, что меня учили в то время, когда ресурсы на компьютерах были крошечными.Я открыт для идеи, что это может быть не важно, в ответах есть несколько веских аргументов против этого.Я начал новый вопрос, исследуя это ... https://stackoverflow.com/questions/6983208/is-micro-optimisation-important-when-coding

Ответы [ 7 ]

51 голосов
/ 05 августа 2011

ОК, так что я запустил тесты, так как с трудом мог поверить, что метод isset () быстрее, но да, и в значительной степени так. Метод isset () работает примерно в 6 раз быстрее.

Я пробовал со строками разных размеров и выполнял разное количество итераций; Соотношения остаются неизменными, а также, кстати, общая длина бега (для строк различного размера), потому что и isset (), и strlen () имеют значение O (1) (что имеет смысл - isset нужно только выполнить поиск в массив C, а strlen () возвращает только счетчик размеров, который хранится для строки).

Я посмотрел это в источнике php и думаю, что примерно понимаю почему. isset (), потому что это не функция, а языковая конструкция, имеет собственный код операции в виртуальной машине Zend. Следовательно, его не нужно искать в таблице функций, и он может выполнять более специализированный анализ параметров. Для тех, кто заинтересован, код находится в файле zend_builtin_functions.c для strlen () и zend_compile.c для isset ().

Чтобы связать это с первоначальным вопросом, я не вижу проблем с методом isset () с технической точки зрения; но имхо труднее читать людям, которые не привыкли к идиоме. Более того, метод isset () будет постоянным во времени, а метод strlen () будет иметь значение O (n) при изменении количества функций, встроенных в PHP. Это означает, что если вы собираете PHP и статически компилируете во многих функциях, все вызовы функций (включая strlen ()) будут выполняться медленнее; но isset () будет постоянным. Однако на практике эта разница будет незначительной; Я также не знаю, сколько таблиц указателей на функции поддерживается, поэтому, если пользовательские функции также оказывают влияние. Кажется, я помню, что они находятся в другой таблице и, следовательно, не имеют значения для этого случая, но прошло уже много времени с тех пор, как я в последний раз действительно работал с этим.

В остальном я не вижу никаких недостатков в методе isset (). Я не знаю других способов получить длину строки, если не учитывать целенаправленно извилистые, такие как explode + count и тому подобное.

Наконец, я также проверил ваше предложение об упаковке isset () в функцию. Это медленнее, чем даже метод strlen (), потому что вам нужен еще один вызов функции и, следовательно, другой поиск в хеш-таблице. Накладные расходы на дополнительный параметр (размер для проверки) незначительны; как и копирование строки, если она не передана по ссылке.

19 голосов
/ 05 августа 2011

Любая разница в скорости не имеет абсолютно никакого значения.В лучшем случае это будет несколько миллисекунд.

Используйте любой стиль, который лучше всего читается вами и всеми, кто работает над кодом - лично я бы решительно проголосовал за второй пример, потому что в отличие от первого, он делает намерение(проверяя длину строки) абсолютно ясно.

12 голосов
/ 09 августа 2011

Ваш код неполон.

Здесь я исправил его для вас:

if(isset($string[255])) {
    // something taking 1 millisecond
}

vs

if(strlen($string) > 255) {
    // something taking 1 millisecond
}

Теперь у вас нет пустого цикла, но реалистичный.Давайте предположим, что для выполнения чего-либо требуется 1 миллисекунда.

Современный процессор может многое сделать за 1 миллисекунду - это так.Но такие вещи, как произвольный доступ к жесткому диску или запрос к базе данных, занимают несколько миллисекунд - тоже реалистичный сценарий.

Теперь давайте снова рассчитаем время:

realistic routine + strlen() over 1000000 iterations 1007.5193998813629
realistic routine + isset() over 1000000 iterations 1000.29940009117126

Видите разницу?

4 голосов
/ 08 августа 2011

Во-первых, я хочу указать на ответ от Artefacto , объясняющий, почему вызовы функций несут издержки над языковыми конструкциями.

Во-вторых, я хочу, чтобы вы знали о том, что XDebug значительно снижает производительность вызовов функций, поэтому, если вы запускаете XDebug, вы можете получить запутанные числа. Ссылка (Второй раздел вопроса). Таким образом, в производстве (где, мы надеемся, у вас не установлен XDebug), разница еще меньше. Он понижается с 6х до 2х.

В-третьих, вы должны знать, что, хотя есть измеримая разница, эта разница проявляется только в том случае, если этот код выполняется в узком цикле с миллионами итераций. В обычном веб-приложении разница не будет измеримой, она будет шуметь под дисперсионным шумом.

В-четвертых, обратите внимание, что в настоящее время время разработки намного дороже, чем нагрузка на сервер. Разработчик, тратящий даже на полсекунды больше на понимание того, что делает код isset, намного дороже, чем экономия загрузки процессора. Кроме того, нагрузка на сервер может быть намного лучше сохранена путем применения оптимизаций, которые действительно имеют значение (например, кэширование).

2 голосов
/ 15 октября 2012

это последний тест:

function benchmark_function($fn,$args=null)
{
    if(!function_exists($fn))
    {
        trigger_error("Call to undefined function $fn()",E_USER_ERROR);
    }

    $t = microtime(true);

    $r = call_user_func_array($fn,$args);

    return array("time"=>(microtime(true)-$t),"returned"=>$r,"fn"=>$fn);
}

function get_len_loop($s)
{
    while($s[$i++]){}
    return $i-1;
}
echo var_dump(benchmark_function("strlen","kejhkhfkewkfhkwjfjrw"))."<br>";
echo var_dump(benchmark_function("get_len_loop","kejhkhfkewkfhkwjfjrw"));

Возвращенные результаты:

RUN 1:

array (3) {["time"] => float (2.1457672119141E-6) ["return"] => int (20) ["fn"] => string (6) "strlen"} array (3) {["time"] => float (1.1920928955078E-5)["return"] => int (20) ["fn"] => string (12) "get_len_loop"}

RUN 2:

array (3) {["time"] => float (4.0531158447266E-6) ["return"] => int (20) ["fn"] => string (6) "strlen"} array (3) {["time"] => float (1.5020370483398E-5) ["return"] => int (20) ["fn"] => string (12) "get_len_loop"}

RUN 3:

array (3){["time"] => float (4.0531158447266E-6) ["return"] => int (20) ["fn"] => string (6) "strlen"} array (3) {["time"] => float (1.2874603271484E-5) ["возвращено"] => int (20) ["fn"] => строка (12) "get_len_loop"}

RUN 4:

array (3) {["time"] => float (3.0994415283203E-6) ["return"] => int (20) ["fn"] => string (6) "strlen"} array (3){["time"] => float (1.3828277587891E-5) ["return"] => int (20) ["fn"] =>string (12) "get_len_loop"}

RUN 5:

array (3) {["time"] => float (5.0067901611328E-6) ["return"] => int(20) ["fn"] => string (6) "strlen"} array (3) {["time"] => float (1.4066696166992E-5) ["return"] => int (20) ["fn "] => string (12)" get_len_loop "}

1 голос
/ 27 марта 2017

В современных ObjectOriented Web Applications одна строка, которую вы пишете в небольшом классе, легко может быть запущена несколько сотен раз для создания одной веб-страницы.Возможно, вы захотите профилировать свой веб-сайт с помощью XDebug , и вы можете быть удивлены, сколько раз выполняется каждый метод класса.Тогда в реальных сценариях вы можете работать не только с небольшими строками, но и с действительно большими документами размером до 3 МБ или более.Вы также можете встретить текст с нелатинскими символами.Так что в конечном итоге то, что изначально было небольшой потерей производительности, может привести к нескольким сотням миллисекунд при рендеринге веб-страницы.

Так что я очень заинтересован в этой проблеме и написал небольшой тест, который будет проверять 4 различных метода для проверкиявляется ли строка действительно пустой "" или на самом деле содержит что-то вроде "0".

function stringCheckNonEmpty0($string)
{
  return (empty($string));
}

function stringCheckNonEmpty1($string)
{
  return (strlen($string) > 0);
}

function stringCheckNonEmpty1_2($string)
{
  return (mb_strlen($string) > 0);
}

function stringCheckNonEmpty2($string)
{
  return ($string !== "");
}

function stringCheckNonEmpty3($string)
{
  return (isset($string[0]));
}

Я обнаружил, что PHP трудно работать с нелатинскими символами, чтобы я скопировал русский текст из ИнтернетаСтраница для сравнения результатов между крошечной строкой «0» и большим русским текстом.

$steststring = "0";

$steststring2 = "Hotel Majestic в городе Касабланка располагается всего в нескольких минутах от "
  . "следующих достопримечательностей и объектов: "
  . "Playas Ain Diab y La Corniche и Центральный рынок Касабланки. "
  . "Этот отель находится вблизи следующих достопримечательностей и объектов: "
  . "Площадь Мухаммеда V и Культурный комплекс Сиди-Бельот.";

Чтобы увидеть разницу, я вызывал каждую тестовую функцию несколько миллионов раз.

$iruncount = 10000000;

echo "test: empty(\"0\"): starting ...\n";

$tmtest = 0;
$tmteststart = microtime(true);
$tmtestend = 0;

for($irun = 0; $irun < $iruncount; $irun++)
  stringCheckNonEmpty0($steststring);

$tmtestend = microtime(true);
$tmtest = $tmtestend - $tmteststart;

echo "test: empty(\"0\"): '$tmtest' s\n";

Результаты испытаний

$ php test_string_check.php
test0.1: empty("0"): starting ...
test0.1: empty("0"): '7.0262970924377' s
test0.2: empty(russian): starting ...
test0.2: empty(russian): '7.2237210273743' s
test1.1.1: strlen("0"): starting ...
test1.1.1: strlen("0"): '11.045154094696' s
test1.1.2: strlen(russian): starting ...
test1.1.2: strlen(russian): '11.106546878815' s
test1.2.1: mb_strlen("0"): starting ...
test1.2.1: mb_strlen("0"): '11.320801019669' s
test1.2.2: mb_strlen(russian): starting ...
test1.2.2: mb_strlen(russian): '23.082058906555' s
test2.1: ("0" !== ""): starting ...
test2.1: ("0" !== ""): '7.0292129516602' s
test2.2: (russian !== ""): starting ...
test2.2: (russian !== ""): '7.1041729450226' s
test3.1: isset(): starting ...
test3.1: isset(): '6.9401099681854' s
test3.2: isset(russian): starting ...
test3.2: isset(russian): '6.927631855011' s

$ php test_string_check.php
test0.1: empty("0"): starting ...
test0.1: empty("0"): '7.0895299911499' s
test0.2: empty(russian): starting ...
test0.2: empty(russian): '7.3135821819305' s
test1.1.1: strlen("0"): starting ...
test1.1.1: strlen("0"): '11.265664100647' s
test1.1.2: strlen(russian): starting ...
test1.1.2: strlen(russian): '11.282053947449' s
test1.2.1: mb_strlen("0"): starting ...
test1.2.1: mb_strlen("0"): '11.702164888382' s
test1.2.2: mb_strlen(russian): starting ...
test1.2.2: mb_strlen(russian): '23.758249998093' s
test2.1: ("0" !== ""): starting ...
test2.1: ("0" !== ""): '7.2174110412598' s
test2.2: (russian !== ""): starting ...
test2.2: (russian !== ""): '7.240779876709' s
test3.1: isset("0"): starting ...
test3.1: isset("0"): '7.2104151248932' s
test3.2: isset(russian): starting ...
test3.2: isset(russian): '7.2232971191406' s

Заключение

  • Обычная функция emtpy() работает хорошо, но не работает со строками типа "0".
  • Функция mb_strlen(), необходимая для проверки текстов с нелатинскими символами, работает хужена больших текстах.
  • Чек $string !== "" работает очень хорошо.Даже лучше, чем функция empty().
  • Но лучшая производительность дает isset($string[0]) Check.

Мне определенно придется работать над всей моей библиотекой объектов.

1 голос
/ 05 августа 2011

Недостаток в том, что isset вообще не является явным, в то время как strlen действительно ясно о ваших намерениях. Если кто-то прочитал ваш код и должен понять, что вы делаете, он может его не заметить, и он не совсем понятен.

Если вы не используете Facebook, я сомневаюсь, что strlen будет там, где ваш сервер будет тратить большую часть своих ресурсов, и вы должны продолжать использовать strlen.

Я только что протестировал strlen намного быстрее isset.

0.01 seconds for 100000 iterations with isset

0.04 seconds for 100000 iterations with strlen

Но это не меняет того, что я сказал сейчас.

Сценарий, как некоторые люди только что спросили:

$string =    'xdfksdjhfsdljkfhsdjklfhsdlkjfhsdjklfhsdkljfhsdkljfhsdljkfsdhlkfjshfljkhfsdljkfhsdkljfhsdkljfhsdklfhlkjfhkljfsdhfkljsdhfkljsdhfkljhsdfjklhsdjklfhsdkljfhklsdhfkljsdfhdjkshfjlhdskljfhsdkljfhsdjkfhsjkldhfklsdjhfkjlsfhdjkflsdhfjklfsdljfsdlkdlfkjflfkjsdfkl';

for ($i = 0; $i < 100000; $i++) {
   if (strlen($string) == 255) {
   // if (isset($string[255])) {
       // do nothing
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...