Q2W
@Q2W

Как замерять производительность кода в продакшне?

Есть бекенд, на котором крутится много веб-приложений.
Хочется достичь двух целей:
1. Снизить время генерации страницы, чтобы гугл и юзеры полюбили наш сайт.
2. Снизить потребление ресурсов CPU, чтобы меньше бекендов нужно было.

Так вот внедряя разные оптимизации в коде мы обычно делаем разные замеры.
Чаще всего замеряем просто среднее кол-во времени, необходимое на обработку одного http-запроса.

Среднее за сутки, т.к. за бОльшее кол-во времени слишком долго ждать. Ну и вообще хотелось бы как-то сделать так, чтобы и суток не нужно было ждать - посмотрел 5 минут, пособирал данные, вычислил среднее, и делаешь вывод - улучшилась производительность или ухудшилась.

Так вот время выполнения сферического алгоритма зависит в т.ч. от нагрузки на сервер. Т.е. можно что-то изменить в коде, выкатить в продакшн, и увидеть, что нагрузка изменилась не в ту сторону, в которую на самом деле изменилась эффективность алгоритма, т.к. просто сейчас часпик или день недели такой, что много запросов отправляют пользователи.

Я попробовал замерять только юзерское время (как утилита time замеряет).
Но когда сервер загружен не на 100%, эта метрика так же чувствительна на загруженности сервера (полупустой сервер работает почти в 2 раза быстрее, чем загруженный на 100%).

Загрузкой на 100% здесь можно считать выполнение стольки однопоточных процессов, съедающих всё доступное время процессора, сколько физических процессорных ядер есть на сервере.
Т.е. там, где включен hyperthreading, нужно запустить таких процессов в 2 раза меньше, чем видно ядер, а где выключен - столько же, сколько видно ядер.

Вот так я замерял:

#!/usr/bin/perl

use strict;
use warnings;
use Time::HiRes qw(time);

my $forks = shift;

my $lscpu = `lscpu`;
my($cpus) = $lscpu =~ /^CPU\(s\):\s+(\d)$/m;
my($threadsPerCore) = $lscpu =~ /^Thread\(s\) per core:\s+(\d)$/m;
my $cores = $cpus / $threadsPerCore;

sub load {
	my $a = 0;
	$a += rand() foreach(0 .. 100000000)
}

fork() for (1 .. $forks);

my $u = - times();
my $t = - time();
load();
$u += times();
$t += time();


printf "| %d | %d | %d | %.2f | %.2f |\n", $cpus, $cores, 2 ** $forks, $t, $u;


И вот мои замеры на машине с hyperthreading:

|----|-----|-----|-------|---------|
|CPUs|Cores|Procs| Time  |User time|
|----|-----|-----|-------|---------|
|  4 |  2  |  1  | 11.08 |  11.07  |
|  4 |  2  |  2  | 11.70 |  11.69  |
|  4 |  2  |  4  | 19.79 |  19.64  |
|  4 |  2  |  8  | 39.42 |  19.62  |
|  4 |  2  |  16 | 83.36 |  19.86  |
|----|-----|-----|-------|---------|


И на машине без hyperthreading:

|----|-----|-----|-------|---------|
|CPUs|Cores|Procs| Time  |User time|
|----|-----|-----|-------|---------|
|  2 |  2  |  1  | 23.74 |  23.73  |
|  2 |  2  |  2  | 23.53 |  23.52  |
|  2 |  2  |  4  | 46.78 |  23.38  |
|  2 |  2  |  8  | 93.76 |  23.43  |
|----|-----|-----|-------|---------|


И на этой машине User time везде примерно одинаков!
Но что же не так с первой машиной? Как только я загружаю на ней больше физических ядер, чем у неё есть, user time увеличивается.
Это что? Магия hyperthreading? Но ведь в htop видно, что во время теста загружено лишь одно виртуальное ядро из 4-х.

Запустил на ещё одной машине с hyperthereading:

|----|-----|-----|-------|---------|
|CPUs|Cores|Procs| Time  |User time|
|----|-----|-----|-------|---------|
|  8 |  4  |  1  | 6.23  |  6.18   |
|  8 |  4  |  2  | 6.20  |  6.16   |
|  8 |  4  |  4  | 8.38  |  8.33   |
|  8 |  4  |  8  | 19.95 |  11.90  |
|  8 |  4  |  16 | 33.71 |  11.98  |
|----|-----|-----|-------|---------|


Тут user time растёт пока не загрузим все 8 виртуальных ядер, а не реальных.
  • Вопрос задан
  • 159 просмотров
Пригласить эксперта
Ответы на вопрос 1
@alejandro68
Все гораздо проще.

Если ваша проблема производительность - то просто логируйте этапы (с указанием времени) обработки каждого запроса. ОТДЕЛЬНОГО запроса. Потом собираете и анализируйте. Да хоть в Excel.

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

Уже оттуда и начнете танцевать про ядра и пр.


Среднее за сутки, т.к. за бОльшее кол-во времени слишком долго ждать.


Достаточно подождать несколько минут, чтобы собрать результаты по десяткам и сотням запросов.


Но что же не так с первой машиной? Как только я загружаю на ней больше физических ядер, чем у неё есть, user time увеличивается.
Это что? Магия hyperthreading? Но ведь в htop видно, что во время теста загружено лишь одно виртуальное ядро из 4-х.


Без спуска на уровень прикладной задачи вряд ли у вас что получится тут узнать.
Это могут быть тупо блокировки.

Да и кто вам сказал, что ваша подсистема спроектирована работать действительно параллельно?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы