В моём коде можно часто увидеть, как я вызываю глобальные функции, используя обратный слэш перед ними: \array_map, \array_filter, \sort и так далее. Это не очень распространённая практика и естественно это вызывает удивление. Каждый раз, когда я попадаю в новую команду или когда к команде присоединяется новый сотрудник, мне задают один и тот же вопрос уже много лет: «зачем ты используешь префиксы для глобальных функций?»

TL;DR
Делаю я это по двум причинам: во-первых, это самодисциплина, а во-вторых, это действительно немного ускоряет работу кода

Микро-оптимизация?

Ты серьёзно?! Это же «экономия на спичках»1! Эта бессмысленная оптимизация не сэкономит ресурсов больше чем в пару наносекунд!

Да, это всё я уже слышал, пожалуйста не утруждайте себя написанием ещё одного такого комментария в мой адрес – я знаю, что я делаю

Давайте начнём с так называемой “оптимизации”. На самом деле она абсолютно не видна в очень маленьких скриптах, которые можно описать в пару строк кода. Даже если мы напишем их пару сотен или тысяч, это действительно вообще ни на что не повлияет – время и количество потреблённой памяти останется на том же уровне. Я ни в коем случае не берусь утверждать, что 100% времени пишу исключительный, эталонный, чистейший код, поэтому настолько в этом преисполнился, что использую даже такую мелочь как префиксы на благо оптимизации кода, даже если при этом переделываю плохой код. Вздор! Конечно же нет – я не редко иду на компромисс с самим собой и пишу паршивый код на быструю руку если нужно очень срочно что-нибудь починить или проверить какую-то концепцию. Эта оптимизация работает только там, где используются большие вычислительные мощности, где происходят рекурсии, где очень часто необходимо обращаться к памяти

Например: (я не могу рассказать всех подробностей, но…) в некоторых проектах, над которыми я работаю уже долгое время, есть участки очень старого кода (10+ лет), постоянно взаимодействующие с громадным количеством данных. Сами файлы занимают по нескольку тысяч строк каждый, в которых происходят всевозможные обращения в базу, фильтрации, рекурсии и ещё много страшных и сложных вещей. Помогают ли там префиксы? Скажем так, я бы слукавил, если бы сказал, что “однозначно да”. Префиксы – это старая привычка, так что я часто неосознанно вставляю их в коде, они составляют меньше 1% от недавно проведённых оптимизаций:

-Before:  47 processes finished in 5.694s (   ~8.25/s)
+ After: 909 processes finished in  0.72s (~1262.5 /s) (x153)
-Before: 2000 processes finished in 446.369s (7'26") ( ~4.5/s)
+ After: 6000 processes finished in 534.663s (8'54") (~11.2/s) (+248%)
-Before: 34 processes finished in 10min (3.4/min)
+ After: 72 processes finished in 10min (7.2/min) (+211%)

Думаю, стоит отметить, что все эти три примера – это разные модули, выполняющие разное количество и объём задач, оптимизированные в разное время, разными способами. Тогда зачем я их сюда добавил? Да так, просто похвастаться (:

Взгляд изнутри

Ладно, шутки в сторону, давайте наконец перейдём к сути. Как PHP понимает, что перед ним глобальная функция, а не пользовательская? Например, если мы напишем следующий код, то препроцессору вообще нет дела до того, поставим мы глобальный префикс или нет:

echo abs(-3.14);
number of ops:  5
compiled vars:  none
line      #* E I O op                     fetch  ext  return  operands
--------------------------------------------------------------------------------
    3     0  E >   INIT_FCALL                                 'abs'
          1        SEND_VAL                                   -3.14
          2        DO_ICALL                           $0      
          3        ECHO                                       $0
          4      > RETURN                                     1

Здесь мы можем смело утверждать, что указание области видимости – избыточно и бесполезно. А что, если мы добавим область видимости в наш код?

namespace App\Test;

echo abs(-3.14);

Отлично! Как раз то, что я и хотел показать. В этом случае, интерпретатор уже начинает поиск вызванной функции:

number of ops:  5
compiled vars:  none
line      #* E I O op                     fetch  ext  return  operands
--------------------------------------------------------------------------------
    5     0  E >   INIT_NS_FCALL_BY_NAME                      'App\Test\abs'
          1        SEND_VAL_EX                                -3.14
          2        DO_FCALL                        0  $0      
          3        ECHO                                       $0
          4      > RETURN                                     1

Таким образом, интерпретатор выполняет следующую проверку:

PHP function search flow

Каждый раз, когда мы используем функцию без указания в какой области видимости она находится, интерпретатор использует операцию INIT_NS_FCALL_BY_NAME вместо INIT_FCALL и вынужден выполнять дополнительные ненужные операции

И всё же, зачем это?

Некоторые разработчики тоже рекомендуют вызывать функции из глобальной области видимости. Другие утверждают, что это сильно влияет на производительность. Не знаю, производит ли это какой-нибудь существенно ощутимый эффект на современные версии PHP, старше 7‑й версии (это, кстати, достаточно сложно проверить), но мне нравится думать, что даже несмотря на всю паршивость окружающего legacy кода, мой свежий код работает эффективнее хотя бы на наносекунду. Я всегда восхищался первопроходцами нашей профессии, которые запускали людей в космос на компьютерах, оперирующих килобайтами, а не гигабайтами, как сейчас. И мне очень жаль, что мы разучились этому мастерству. Теперь нам проще докупить RAM для сервера, чем потратить время на оптимизацию

Допустим, но это ведь всё равно «экономия на спичках»! Никакого прироста производительности мы не получим, если процессор пару тройку раз выполнит пару дополнительных операций. Бизнес не станет ждать, пока ты наиграешься в оптимизацию

Да, это действительно так. Причина по которой я до сих пор так делаю, несмотря на то, что PHP уже давно обзавёлся JIT и OPcache – это самодисциплина. Написать ещё один обратный слэш, пока пишешь код, не занимает очень много времени. Объяснение этой привычке очень простое: я всегда думаю о том, что другие люди будут читать мой код и я всегда стараюсь написать код как можно чище, по возможности исправив предыдущий. Для меня это так же символично, как вмятина на бумаге, которую оставляет кольцо канадского инженера. Старый код, с которым я работаю, иногда преподносит сюрпризы, вроде переопределённых глобальных методов (overridden methods). Таким образом я хочу, чтоб я и мои коллеги понимали назначение функции “a priori”, не проверяя её реализацию

namespace App\Test;

function pi() {
    return 2.718281828459045;
}

echo pi(); // 2.718281828459045
echo PHP_EOL;
echo \pi(); // 3.14159265358979
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
function name:  (null)
number of ops:  9
compiled vars:  none
line      #* E I O op                     fetch  ext  return  operands
--------------------------------------------------------------------------------
    9     0  E >   INIT_NS_FCALL_BY_NAME                      'App\Test\pi'
          1        DO_FCALL                        0  $0      
          2        ECHO                                       $0
   10     3        FETCH_CONSTANT                     ~1      'App\Test\PHP_EOL'
          4        ECHO                                       ~1
   11     5        INIT_FCALL                                 'pi'
          6        DO_ICALL                           $2      
          7        ECHO                                       $2
          8      > RETURN                                     1

Function app\test\pi:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
function name:  App\Test\pi
number of ops:  2
compiled vars:  none
line      #* E I O op                     fetch  ext  return  operands
--------------------------------------------------------------------------------
    6     0  E > > RETURN                                     2.71828
    7     1*     > RETURN                                     null

  1. Есть выражение «экономия на спичках». Если кратко, то оно подразумевает, что тратятся значительные усилия на экономию или оптимизацию там, где в общем масштабе не получится добиться хоть сколько-нибудь значительного выигрыша в ресурсах ↩︎