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

Я наткнулся на этот код …

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

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

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

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

Solutions Collecting From Web of "isset () vs strlen () – вычисление быстрой / четкой длины строки"

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

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

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

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

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

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

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

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

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

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

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

против

 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")); в 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) ["return"] => int (20) ["fn"] => string (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"}

Недостатком является то, что 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 } } 

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

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

В современных объектно-ориентированных веб-приложениях одна строка, которую вы пишете в небольшом классе, может быть запущена несколько раз в несколько раз для создания единой веб-страницы.
Возможно, вы захотите профилировать свой веб-сайт с помощью 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 = "Отель 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() функция emtpy() работает хорошо, но не работает на строках типа «0».
  • Функция mb_strlen() , необходимая для проверки текстов с не-латинскими символами, хуже на больших текстах.
  • $string !== "" Check $string !== "" работает очень хорошо. Даже лучше, чем empty() функция.
  • Но лучшая производительность дает isset($string[0]) Check.

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