Многие в курсе что при параллельных запросах от браузера к серверу, браузер старается ограничить число активных запросов (по ~5 на домен). Но та же проблема возникает и на бэкэнде с ресурсами
Одна проблема с БД решается транзакциями. Но вторая, более явная остаётся - сессии. В моём случае эта проблема вылезла при загрузке файлов в SPA-приложении. Делаете 20 POST запросов с файлами и параллельно ходите по сайту. Выглядит всё так, что покуда файл не загрузится и сервер не ответит, следующий GET не сработает и UI не обновится
Происходит это потому что php блокирует потоки где есть session_start(). В моём случае это все запросы, потому что есть авторизация
Решается очень просто:
session_write_close();
Закрываем явно запись в сессию. Читать мы всё ещё сможем оттуда, но зато следующие запросы смогут быстро исполняться. Я это делаю ещё до того как файл начнётся обрабатываться
PHP как язык плохо подходит для работы бинарными данными напрямую. Но иногда приложения должны взаимодействовать по таким протоколам, где размер пакетов очень важен или родным форматом данных для какого-то приложения, который никем в красивый json или xml не переводится.
Напоминаю какие типы данных есть в Си, на котором основан php
Тип
Размер памяти
Значений всего
char
1 байт = 8 бит
256
int
2 байт = 16 бит
2^16=65536
short
2 байта
long
4 байта = 32 бита
4294967295
Char при этом используется универсально согласно ASCII табличке как в качестве явного кодирования текста, так и вспомогательными маркерами. Про float я умолчу, ибо мне не понадобилось.
Если с бинарными данными не работать, то можно и забыть основы языка. С целыми числами на основании 10 всё понятно, но обычно значения длинных данных в них не пишут. Это объясняет табличку выше.
Бинарная, например 0b1011
Восьмеричная, например 0123
Шестнадцатиричная, например 0xF560B1A9
Кроме этого, если вы переписываетесь с коллегами которые пишут на си, то они могут обозначать приставками или окончаниями
Unsigned int 14u
Long double или long int. Например 1.2l или 1L - фиг поймёшь, это единица или long
Float (с плавающей запятой) 1.3f или 1.3F
Итого, у нас char a будет хранить значение от -128 до 127.. ну или 256 если это extended ASCII. Отлично. Теперь как это использовать в php?
Честно, для меня было откровением что порядок в данных имеет значение. Я привык что 123 уже подразумевает где сотни, десятки и единицы, но для компьютера ведь всё равно. Конечно одно дело порядок написания для человека.. но тут другое - порядок записи байтов в зависимости от адреса. И больше всего удивительно что архитектуры на уровне работы с памятью разделились - x86 на стороне little endian, а за big endian SPARC и прочие. Поэтому если вы интегрируете разные архитектуры с бинарным форматом данных - договоритесь которая система будет работать.
2. Битовые маски - популярный метод хранить много булевых значений в одной переменной и включать/проверять с помощью AND/OR операций. Если вы когда либо выполняли chmod 755, то уже включали эти флажки на привилий.
3. Побитовые сдвиги - помогают работать с битовыми масками и с битами в вообще
$b = $a **>>** 2; - сдвиг битов на две позиции вправо, тут может пригодится константа PHP_INT_SIZE $b = 8 >> 3; //1
Проверка данных с помощью контрольных сумм увы для меня осталась за пределами понимания. Важно знать что биты суммируются, и что полиномы бывают разные. Соответсвенно алгоритмы тоже бывают разные и как правило неограничивается функцией crc32(). Гляньте на мою коллекцию и онлайн валидатор .
Если вы обычную переменную в php начнёте сохранять в файл, то она наверняка не будет оптимальной. А если таких однородных данных много - то тем более имеет смысл создать более компактную версию.
Например, если вы хотите число 250 записать в один байт как char-тип, вместо того что-бы писать "строкой" в три символа, как то будет делать php по умолчанию, пишем:
pack('c', 250);
Первый аргумент это формат данных по длине. Если все данные char-типа, то можно написать c* как в регулярных выражениях, получить повторение типа. Можно написать c4, что будет аналогично cccc (четыре повторения данных char-типа)
Всего типов данных много..
a - строка, свободные места в поле заполняются символом с кодом 0
A - строка, свободные места заполняются пробелами
h - шестнадцатеричная строка, младшие разряды в начале (little endian)
H - шестнадцатеричная строка, старшие разряды в начале (big endian)
c - 1 байт (signed char)
C - 1 байт (unsigned char)
x - символ с нулевым кодом
X - возврат назад на 1 байт
@ - заполнение нулевым кодом до заданной абсолютной позиции
Float
f - число с плавающей точкой
d - число двойной точности
2 байта (целые, integer)
4 байта (long)
- s - short (16 bit)- S - unsigned short- n - short (big endian)- v - unsigned short (little endian)- i - integer (размер и порядок байтов определяется архитектурой)- I - unsigned integer
- l - знаковое длинное целое (32 бита, порядок знаков определяется архитектурой)- L - беззнаковое длинное целое- N - беззнаковое длинное целое (32 бита, старшие разряды в конце)- V - беззнаковое целое (32 бита, младшие разряды в конце)
Для раскодирования используется unpack() с аналогичным форматом типов данных, только теперь можно добавлять названия ключей для результатов ассоциативного массива unpack("cmessageid/Vtimestamp", pack("H*",FFFF));
Заметьте что pack я тут использую вместо hex2bin, которая недоступна для версий php менее 5.4
base_convert - конвертирование строковых представлений ч исел из любого основания в другое (скажем 16 в 2)
bindec, decbin - конвертирование [2-10] двоичных и десятичных данных
octdec, decoct - конвертирование [8-10] десятичных и восьмеричных данных
hexdec, dechex - конвертирование [16-10] десятичных и шестнадцатиричных данных
ord, chr - конвертирование [256-10] десятичных и символьных (ascii) данных
base64_encode, base64_decode - конвертирование [256-256] данных, но в отличие от предыдущих форматов, данные одного значения не кодируются в 6 битах что-бы получить 64 значения, а по прежнему в 8 битах - остальные символы просто не используются.. Из-за этого формат менее эффективен в хранении, но для человека в виде текста более компактен чем нули, единицы или hex.
Отличительная и неприятная особенность, конечно в том что каждая из функций конвертирует данные со строго определённым размером данных. Обычно же у нас есть какой-то пакет данных. Их можно проконвертировать в цикле с определённым шагом..
Профилирование это анализ потребления ресурсов при работе программы. Этимология слова видимо связана с тем что профиль это некая граница, отсюда - поиск границ компонентов ПО в ресурсном пространстве. В моём случае программа это исполняемые php-скрипты, а ресурсы это время, память и нагрузка на процессор. Время исполнения не всегда связано с нагрузкой процессора. Процесс может ждать IO-ответа от более медленных источников, а может и просто спать.
Я писал про микрооптимизацию в php. Как правило отмечали критики, в общем случае она не нужна. Профилирование это взгляд с высоты птичьего полёта на подключаемый код. Он делается только на рабочей машинке, а не на production. В некоторых LAMP-сборках инструменты для этого уже установлены.
Он позволяет генерировать доклад о профилировании в форме файла. Из-за глубокой иерархичности, доклад обычно весит несколько мегабайт. Настройки модуля хранятся в php.ini, там же можно и указать путь к результату профилирования. Назовём его cachegrind.out - он совм естим с более общим форматом valgrind
Можно сделать так что-бы он генерировался постоянно, можно - с помощью куки-триггеров. Файл можно делать на каждый отдельный системный процесс, на отдельный URL запрос, по времени исполнения или их комбинаций. Дальше его стоит связать с одним из анализаторов:
XHProf - пакет PECL, написан в Facebook, поставляется тоже как модуль (xhprof.so или xhprof.dll). Позволяет учитывать значительно больше параметро в, в т.ч. нагрузку на CPU и оперативную память. Позволяет сравнивать повторные запуски и агрегировать их результаты. Наиболее полезная фича - суммарный анализ по времени в виде весового графа. Граф генерируется с помощью graphviz и утилитки dot. У меня были небольшие проблемы под маком, пришлось поставить вручную.
Поскольку просмотр результатов xhprof целиком web-based, то надо залинковать где-то в корне htdocs путь к xhprof_html папке. Для подключения, необходимо изменять свои скрипты. Т.е. где-то в начале своего index.php ставится include, инициализация и что-бы не бояться за какой-то exit, хук на register_shutdown_function. Вместо того что-бы инклудить каждый раз, можно воспользоваться php-директивой auto_prepend_file и включать это файл всегда
//результат будет виден с ?xhprof=1 if (extension_loaded('xhprof') && $_GET['xhprof']) { include_once 'xhprof_lib/utils/xhprof_lib.php'; include_once 'xhprof_lib/utils/xhprof_runs.php'; xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY); function end_xhprof(){ $profiler_namespace = 'myapp'; // namespace вашего приложения $xhprof_data = xhprof_disable(); $xhprof_runs = new XHProfRuns_Default(); $run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace); //Замените относительный путь к залинкованной папке $profiler_url = sprintf('http://' . $_SERVER['SERVER_NAME'] . '/xhprof/index.php?run=%s&source=%s', $run_id, $profiler_namespace); echo 'XHProfiler output'; } register_shutdown_function('end_xhprof'); }
Профайлеры конечно хороши когда есть начало, конец и результат в виде доклада. Но когда php висит как демон очень долго и хочется понять где же это он тормозит.. В утилитках типа phpcs для вывода деталей работы, часто существует флаг -v (verbose mode). Но до того как я это узнал, мой процесс уже час как висел в jenkins'е и я незнал, стоит ли мне убивать его, или он что-то полезное таки делает. К счастью, в общем случае, можно следить за IO-действиями любого процесса без его согласия с помощью strace:
strace -p IDпроцесса strace php index.php #запустит трейс вместе за запуском php в CLI режиме
PHPStorm умеет искать использование методов и классов, но это статический анализ, а вот для того что-бы динамически построить дерево зависимостей, ес ть отличный pecl модуль inclued (на момент установки у меня - 0.1.3)