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 ответов:
хорошо, поэтому я провел тесты, так как я едва мог поверить, что метод 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 !== ""
работает очень хорошо. Даже лучше, чем