isset () vs strlen () - быстрый / четкий расчет длины строки


я наткнулся на этот код...

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

isset () находится между 6 и 40 быстрее, чем

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

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

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

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

8 53

8 ответов:

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

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

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

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

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

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

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

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

ваш код является неполным.

вот, я исправил это для вас:

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

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

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

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

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

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

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

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"));

результаты:

выполнить 1:

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

выполнить 2:

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

"выполнить" 3:

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

выполнить 4:

array (3) { ["time"]= > float(3.0994415283203 E-6) ["returned"]=> int (20) ["fn"]=> string (6) " strlen" } массив(3) { ["time"]= > float (1.3828277587891 E-5) ["returned"]=> int(20) ["fn"]=> string(12)" get_len_loop"}

выполнить 5:

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

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

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

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

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
   }
}

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

поэтому я очень заинтересован в этой проблеме и написал небольшой тест, который проверил бы 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".
  • The mb_strlen() функции, которые необходимо проверить на текстах с нелатинскими символами хуже работает на больших текстах.
  • Регистрация $string !== "" работает очень хорошо. Даже лучше, чем

Если вы хотите сохранить ясность, вы можете сделать что-то вроде:

function checklength(&$str, $len)
{
     return isset($str[$len]);
}