Олег Цилюрик - QNX/UNIX: Анатомия параллелизма
cout << ", server = " << data.cps << endl;
tim = new uint64_t[num];
uint64_t tim2;
uint8_t *bufin = new uint8_t[blk];
*bufou = new uint8_t[blk];
// определяем дескриптор сетевого узла
int32_t node = netmgr_strtond(PATH, NULL);
// это интересное место: если в имени нет сетевого префикса пути,
// но это имя удается открыть, то это локальный хост!
if (node == -1 && fd > 0 && errno == ENOENT)
node = ND_LOCAL_NODE;
// по адресным данным, полученным ранее по read(), создаем канал
// для прямого обмена сообщениями с тем же процессом:
int coid = ConnectAttach(node, data.pid, data.chid, _NTO_SIDE_CHANNEL, 0);
if (coid < 0) exit("connect to message channel");
cout << " - message exchange:" << flush;
// обмен по каналу низкоуровневых сообщений
for (int i = 0; i < num; i++) {
tim[i] = ClockCycles();
if (MsgSend(coid, bufou, blk, bufin, blk) == -1)
exit("exchange data with channel");
tim[i] = ClockCycles() - tim[i];
}
outtim();
ConnectDetach(coid);
// повторяем в точности тот же обмен, но по запросу devctl()
unsigned int DCTL = (blk<<16) + DCMD_SRR;
cout << "- manager exchange:" << flush;
for (int i = 0; i < num; i++) {
tim[i] = ClockCycles();
if (devctl(fd, DCTL, bufou, blk, NULL) ! = EOK)
exit("DEVCTL error");
tim[i] = ClockCycles() - tim[i];
}
outtim();
close(fd);
delete [] bufin;
delete [] bufou;
delete [] tim;
return EXIT_SUCCESS;
}
Смотрим локальные результаты исполнения и оценки, которые дает нам эта клиентская программа (знаком отмечено С.К.О. предшествующего ему в выводе значения измеренной средней величины, после чего в скобках — процентное отношение этого С.К.О. к измеряемой величине):
# nice -n-19 cli -b1 -m1000
SRR client: vers. 1.03
server path: /dev/srr, block size = 1 bytes, repeat = 1000
CPU speed [c.p.s.]: client = 534639500, server = 534639500
- message exchange: 2693 ~168 {6%}
- manager exchange: 6579 ~357 {5%}
# nice -n-19 cli -b10 -m1000
SRR client: vers. 1.03
server path: /dev/srr, block size = 10 bytes, repeat = 1000
CPU speed [c.p.s.]: client = 534639500, server = 534639500
- message exchange: 2726 ~258 {9%}
- manager exchange: 6725 ~378 {6%}
# nice -n-19 cli -b100 -m1000
SRR client: vers. 1.03
server path: /dev/srr, block size = 100 bytes, repeat = 1000
CPU speed [c.p.s.]: client = 534639500, server = 534639500
- message exchange: 3351 ~190 {6%}
- manager exchange: 7055 ~414 {6%}
# nice -n-19 cli -b1000 -m1000
SRR client: vers 1.03
server path: /dev/srr, block size = 1000 bytes, repeat = 1000
CPU speed [c.p.s.]: client = 534639500, server = 534639500
- message exchange: 3912 ~369 {9%}
- manager exchange: 8312 ~4024 {48%}
# nice -n-19 cli -b4000 -m1000
SRR client: vers 1.03
server path: /dev/srr, block size = 4000 bytes, repeat = 1000
CPU speed [c.p.s.] client = 534639500, server = 534639500
- message exchange: 5393 ~518 {10%}
- manager exchange: 10666 ~770 {7%}
# nice -n-19 cli -b6000 -m1000
SRR client vers 1.03
server path /dev/srr, block size = 6000 bytes, repeat = 1000
CPU speed [c.p.s.]: client = 534639500, server = 534639500
- message exchange: 7373 ~612 {8%}
- manager exchange: 12423 ~995 {8%}
# nice -n-19 cli -b1000 -m1000
SRR client: vers. 1.03
server path /dev/srr, block size = 10000 bytes, repeat = 1000
CPU speed [c.p.s.]: client = 534639500, server = 534639500
- message exchange: 14365 ~953 {7%}
- manager exchange: 16018 ~5399 {34%}
Это дает нам следующую информацию:
• Обмен с сервером, работающим на локальном хосте, происходит синхронно: клиент, переслав запрос серверу, блокируется в ожидании ответа от него. В этих условиях мы загружаем процессор на 100% совместной активностью клиента и сервера.
• Обмен в эквивалентных условиях с сервером, работающим как менеджер ресурса, требует (в сравнении с прямым обменом сообщениями) в 1,12–2,44 раз большее количество процессорных циклов на свое обслуживание, или, в относительных единицах, максимально достижимая производительность менеджера меньше на 12–144% .
• Самые неблагоприятные (144%) значения относятся к случаю обмена короткими сообщениями (1–10 байт); достаточно ощутимое (~2) значение этого соотношения сохраняется до размеров передаваемых блоков данных, равных 8–10 Кбайт.
• Накладные расходы на передачу единичного байта информации недопустимо велики (2693 циклов на байт при обмене сообщениями и 6579 циклов на байт — для менеджера) при организации обмена короткими сообщениями. С ростом объема данных, передаваемых за один цикл обмена, этот показатель очень резко падает (на блоках по 100 байт уже 33,5 и 70 соответственно, т.е. 2 порядка). Для систем с интенсивными потоками обмена необходимо стремиться максимально блокировать передаваемые данные и минимизировать число актов обмена.
Теперь выполним то же самое, но при обмене с сервером, локализованным на удаленном хосте сети (мы используем низкоскоростную сеть 10 Мбит/сек, на которой все эффекты более наглядны):
# nice -n-19 cli -nrtp -b1 -m500
SRR client: vers. 1.03
server path: /net/rtp/dev/srr, block size = 1 bytes, repeat = 500
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 671017 ~391587 {58%}
- manager exchange: 712181 ~394844 {55%}
# nice -n-19 cli -nrtp -b10 -m500
SRR client: vers. 1.03
server path: /net/rtp/dev/srr, block size = 10 bytes, repeat = 500
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 642456 ~380313 {59%}
- manager exchange: 743094 ~423717 {57%}
# nice -n-19 cli -nrtp -b100 -m500
SRR client: vers. 1.03
server path: /net/rtp/dev/srr, block size = 100 bytes, repeat = 500
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 878686 ~432230 {49%}
- manager exchange: 907474 ~420140 {46%}
# nice -n-19 cli -nrtp -b1000 -m500
SRR client: vers. 1.03
server path: /net/rtp/dev/srr, block size = 1000 bytes, repeat = 500
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 2064542 ~358333 {17%}
- manager exchange: 2113638 ~372487 {18%}
# nice -n-19 cli -nrtp -b3000 -m200
SRR client: vers. 1.03
server path: /net/rtp/dev/srr, block size = 3000 bytes, repeat = 200
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 4134249 ~418168 {10%}
- manager exchange: 4181481 ~418139 {10%}
# nice -n-19 cli -nrtp -b5000 -m200
SRR client: vers. 1.03
server path: /net/rtp/dev/srr, block size = 5000 bytes, repeat = 200
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 5805056 ~252663 {4%}
- manager exchange: 5825837 ~229120 {4%}
# nice -n-19 cli -nrtp -b8000 -m200
SRR client: vers. 1.03
server path /net/rtp/dev/srr, block size = 8000 bytes, repeat = 200
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 8741090 ~446299 {5%}
- manager exchange: 8788642 ~427459 {5%}
# nice -n-19 cli -nrtp -b10000 -m200
SRR client: vers. 1.03
server path: /net/rtp/dev/srr, block size = 10000 bytes, repeat = 200
CPU speed [c.p.s.]: client = 534639500, server = 451163200
- message exchange: 8971296 ~451857 {5%}
- manager exchange: 9731224 ~301409 {3%}
В этом варианте основной компонент задержки вносится передачей данных по физическому каналу; разница между реализациями обмена сообщениями и менеджера ресурсов в значительной степени нивелирована.
Наш второй клиент (файл clr.cc), неизменно работающий с тем же сервером, весьма похож на предыдущий, но он массированно «гонит» поток данных на сервер, пользуясь только одним из механизмов (ключ -d) до прекращения его выполнения пользователем по ^C. Результат его работы — средняя плотность потока информации за весь интервал работы.
Второй клиентский процесс#include "common.h"
static bool conti = true;
// завершение процесса по сигналу пользователя (SIGINT - ^C)
inline static void trap(int signo) { conti = false; }
int main(int argc, char **argv) {
cout << "SRR repeater: " << VERSION << endl;
int opt, val;
unsigned int blk = 100;
char PATH[_POSIX_PATH_MAX] = "";
bool lowlvl = true;
while ((opt = getopt(argc, argv, "n:b:d")) != -1) {
switch(opt) {
case 'n': // имя сетевого узла
strcpy(PATH, "/net/");
strcat(PATH, optarg);
break;
case 'b': // размер блока данных
if (sscanf(optarg, "%i", &blk) ! = 1)
exit("parse command line failed");
break;
case 'd': // обмен сообщениями
lowlvl = false;
break;
default:
exit(EXIT_FAILURE);
}
}
strcat(PATH, DEVNAME);
cout << "server path: " << PATH
<< ", block size = " << blk << " bytes" << endl;
// при инициализации мы сразу получаем скорость процессора клиента
result data;
cout << "CPU speed [c.p.s.]: client = " << data.cps;
uint64_t cps = data.cps;
// пытаемся подключиться к серверу-менеджеру
int fd = open(PATH, O_RDONLY);