LINUX.ORG.RU

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


2

3

Суть задачи: RedHat Linux MRG Realtime Есть структура, содержащая (условно) список сокетов, в которые надо писать одинаковые данные (мультиплексирование). Сокетов достаточно много - сотни и единицы тысяч.

Операции записи должны быть асинхронными, при этом потокобезопасными. Высокие требования к латентности (распространение рыночных данных).

На какие низкоуровневые средства предложите посмотреть и какие высокоуровневые реализации посоветуете посмотреть?

Спасибо заранее.

Ответ на: комментарий от ae1234

если это както улучшить апаратными методами

аппаратная поддержка уже лет 10 минимум есть, но она голимая (слишком много нюансов, слишком много осей, слишком мало преимуществ даёт). Да и всё равно оверхед будет всегда.

Поэтому такие вещи как отложенные системные вызовы (когда делаешь подрять сотню write, но реально они будут выполнены несколько позже и за один присест) очень даже нужны для приложений подобного рода.

Вообще, судя по форуму, не помешало бы сделать ревизию архитектуры писюков в сторону более предсказуемых задержек. Тут явно дело не в процах, а в окружающей обвязке.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

поддержка - не есть улучшение апаратными методами переключение двух контекстов - на редкость тормазная операция происходит сброс кэшей - перезагрузка таблиц и так далее

ae1234 ★★
()
Ответ на: комментарий от true_admin

Если дадите ссылки для отложенных системных вызовов применительно к моей ситуации, буду признателен, потестю такой вариант тоже :)

Ну и варианты типа аналогов сисколлов lio_* которые выше упоминались :)

westtrd
() автор топика
Ответ на: комментарий от westtrd

судя по гуглу, это так и не реализовали (во всяком случае в стандартном ядре), хотя пару лет назад об этом была новость.

true_admin ★★★★★
()
Ответ на: комментарий от westtrd

затестил
2 мелких сервера на квеоне 2.4 ггрц - между ними 3com свитч управляемый

1) aio можно выбрасывать сразу - оно работает через нити юзерские

2) через обычьные сисколы получилось около 150тон пакетов в секунду - при 128 байтах полезных данных - это около 28мегабайт в секунду - тоесть при отправле канал нагружаеться на 25% от своего теор максимума

ядро процессора при этом напрягалось от силы на 15%

затестю еще mwritev

ae1234 ★★
()
Ответ на: комментарий от ae1234

ядро процессора при этом напрягалось от силы на 15%

во что же тогда упёрлось? 150килопакетов в секунду это уровень моего реалтека.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

твой реалтек - тут непричем - пакет формирует процессор а не сетевая

с 15% я соврал - забыл что передача то импульсная - а 15 это средняя за секунду
такчто выходит что при передаче 100% загрузка

пакетик каждые 6 микросекунд

посмотрим сколько выдаст ядерный модуль :)

ae1234 ★★
()
Ответ на: комментарий от ae1234

такчто выходит что при передаче 100% загрузка

так по-любому процессор либо свободен либо занят. То что топ как idle отображает это общее время простоя за большой период.

А как тестирование проходило?

true_admin ★★★★★
()
Ответ на: комментарий от westtrd

протестировал через ядерный модуль
vwritev

как неудивительно но результат практически совпал с результатом автора
выйгрыш есть - но он в районе 15%

при 1000 клиентах

write()
0.007281секунды на запись во все сокеты

mwritev()
0.006092секунды - * - *


если модуль переделать на более простой и или на заюзывание sendfile то наверно можно еще выжать процентов 10
но неболее

там даже отключение фаервола (на исходящей машине) улучшало результат :)
ну и привязывание к одному ядру - также улучшало
./a.out 1000
0 0.000540
999 0.007087
999 0.013894
999 0.011304
999 0.007069
999 0.013796
999 0.011735
999 0.006790
999 0.013897
999 0.011402

schedtool -a 0x2 -e ./a.out 1000
0 0.000025
981 0.006169
981 0.007108
999 0.011327
999 0.007700
999 0.007447
999 0.008839
999 0.006723
999 0.007369

ae1234 ★★
()
Ответ на: комментарий от true_admin

ну - тамже идет общение с реальной железкой - с сетевой картой
оно вполне может просто ожидать что то - прерывания иль еще какой хрени

ae1234 ★★
()
Ответ на: комментарий от westtrd

попробовал щас с отключение (tcp_nodelay) - разультат почти тотже

вообщемто результат предсказуемый 150тон сисколов - на 100% загрузку ядра это не такая большая цифра чтобы накладные расходы на сискол сильно влияли сами сисколы - как какунить простую операцию - делают по миллиону+ в секунду

а вот что будет при одновременной передаче таких пакетов с нескольких ядер - даж и не знаю - с одной стороны несколько процессорных ядер - а с другой стороны борьба за ресурсы и синхронизация кернельных структур от нескольких ядер

ae1234 ★★
()
Ответ на: комментарий от ae1234

Вы тут разряды не перепутали? средний вызов 7 микросекунд - как то многовато, или я что то не так понял?

westtrd
() автор топика
Ответ на: комментарий от westtrd

на ксеоне 2.4 - 7 микросекунд -> 150тысяч пакетов в секунду - при 168 байтном пакете это примероно 25 мегобайт в секунду (четверть гигабита)

был бы каконить i7 на 4.5ггрц - было бы 3 микросекунды :) наверно

а в чем сомнения то ?

ae1234 ★★
()
Ответ на: комментарий от ae1234

Просто сопоставил это с реально замеренными результатами на латентность через TCP одной и той же машины у ZMQ - они вписывались в 5 микросекунд доставки

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

И еще, не смотрели ли в сторону lio* вызовов?

westtrd
() автор топика
Ответ на: комментарий от westtrd

zmq на одной и тойже машине а как тестировали то ? было 2 общающихся процесса - или отсылка и прием сообщения было в одном процессе ?

а насчет aio - я именно и lio и завел в работу - можно выкидывать на линуксе это проводит к порождение отдельного треда - и передача запросов на обработку просто напросто туда - где она в обычьном (наверно) юзерспейсы и дергает сисколы покрайней мере - скорость была раза в 2 выше - отсылки пакетов - чем при обычьном write

ae1234 ★★
()
Ответ на: комментарий от ae1234

ZMQ тестился через 2 отдельных приложения через стек TCP-IP

westtrd
() автор топика
Ответ на: комментарий от ae1234

Есть еще одна мысль - изначально буфер поместить в ядро через mmap() и пользовать уже zero-copy

Как соберу стенд, буду пользовать и пробовать, это уже начинает смахивать на критически важный компонент моего проекта :)

westtrd
() автор топика
Ответ на: комментарий от westtrd

все эти хитрые атомарные блокировки и алгоритмы спинов - в реальности означают 2 пайпа - в которые 2 процесса пишут и читают

и лиш только при беспрерыной работе - процессов - есть вариант обмена инфы - который означает не ждать сообщения - а изредка проверять флаг - поступило ли новое или нет

а самое простое общение двух процессов

#include <stdio.h>
#include <stdlib.h>

main(){
    int i,j;
    int fd1[2],fd2[2];
    pipe(fd1,0);pipe(fd2,0);
    if(!fork())
        for(j=0;j<1000000;j++){
            read (fd1[0],&i,sizeof(i));
            write(fd2[1],&i,sizeof(i));
            }
    else
        for(j=0;j<1000000;j++){
            write(fd1[1],&i,sizeof(i));
            read (fd2[0],&i,sizeof(i));
            }
    }
вот эта прога выполняеться 1мильон раз за 8.2 секунды у меня - за это время процеесы отправляют по очереди друг другу 2 миллиона сообщений

8.2c/2000000=4.1 микросекунды на отправку+прием сообщения но это внутри процессора

а вот - из процесса да и в сетевую - это занимает 7микросекунд только отправка

ae1234 ★★
()
Ответ на: комментарий от westtrd

zero-copy тож протестил - выйгрыша практически нет - 128байт данных это очень мало для этого

там оказалось проще можно сделать - через sendfile - открыть файлик на чтения какойнить - и sendfil-ить первые 128 в каждый сокет

ae1234 ★★
()
Ответ на: комментарий от ae1234

Тогда встают вопросы следующего плана, где узкое место: 1. стек протоколов 2. переключение контекста в ядро 3. коммуникации внутри ядра между shared memory и стеком протоколов 4. оверхед на сам цикл - но в это я мало верю :)

westtrd
() автор топика
Ответ на: комментарий от westtrd

дай догадаюсь - это когда 2 процесса постоянно работают и постоянно проверяют флаг (ячейка в памяти) ? :) это мягко говоря не совсем честная работа

суть то в том - что пробудить спящий процесс способно только ядро - а чтоб это сделать нужно сделать контент свитчь - а он тормазной

ae1234 ★★
()
Ответ на: комментарий от westtrd

а почему ты взял что там гдет есть узкое место ? оно пакет формирует - tcp подстсраивает на эту сессию - и через драйвер передает его в сетевую и так далее (оффлоады пробовал включать отключать - сильно не влияют)

ae1234 ★★
()
Ответ на: комментарий от ae1234

Кстати, коль уж мы затеяли разговор про модули ядра, как вам такой вариант: 1. пишется модуль ядра которому: - из юзерспейса через zero-copy передается список сокетов

- совместно с блоком данных переменного размера

- эти параметры передаются в системных вызовах

- блок данных аллоцирован через mmap, при этом таких блоков может быть несколько, но без фанатизма

2. этот модуль садится эксклюзивно на ядро или несколько ядер и пишет в сокеты без передыху

3. если что то с неким сокетом не так, тут надо думать в плане обратной коммуникации :)

westtrd
() автор топика
Ответ на: комментарий от westtrd

консумер - менеджер - за этими словами прячется тупой цикл который проверяет ячейку памяти - как тока ячейка изменилась - значит в какой то области памяти лежит сообщение а тот кто посылает - записывает сообщение - потом меняет флаг - и дергает сброс кешей процессора это крайне крайне простое решение и крайне мало применимое - просто напросто цикл проверки ячейки постоянно жрет ядро на 100% а это потеря ресурсов

а насчет идеи 1) zero-copy список сокетов ? это КАК :) зеро копи это как раз без участия юзерспейса 2) а ЗАЧЕМ вам писать без передыху в сокеты ? у вас всеравно нет гигабита на юзеров то

и вообще - скорость будет такая же

вы понимаете - что даже одно ядро древнего 2.4грц ксеона - нагружает гигабит на четверть при передаче этих пакетов вы понимаете - что при отсутвии гигабита чистого до юзеров - со всеми этими пакетами будут происходить занятные штуки при прохождение девайсов ? ладно если они только замедляться и их железка (скажем 100мбитный стык с провайдером) просто замедлит с 25мегабайт в секунду до 10 мегабйт в секунду а если буферов нехватит у передаточных? пакеты будут тупо теряться и ретрансмиты - рандомных сессий такое замедление покажут что там не до микросекунд

ae1234 ★★
()
Ответ на: комментарий от westtrd

мне почемуто кажеться - что вы иль хотите странного - иль непонимаете чего хотите
?

ae1234 ★★
()

Говорил же тебе, покопайся с нетграфом. Вот, что тебе нужно:

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netgraph.h>
#include <netgraph/ng_message.h>
#include <netgraph/ng_tee.h>
#include <netgraph/ng_one2many.h>
#include <netgraph/ng_ksocket.h>

struct sock_pair
{
	int cs, ds;
};

struct ng_one2many_config one2manyconf = {NG_ONE2MANY_XMIT_ALL,
	NG_ONE2MANY_FAIL_MANUAL};

void time_handler(struct sock_pair* sp)
{
	time_t itime;
	char* tstr;

	itime = time(NULL);
	tstr = ctime(&itime);
	if (NgSendData(sp->ds, "pupsup", (u_char*)tstr, strlen(tstr)) < 0)
		perror("NgSendData");
}

void c_read_handler(struct sock_pair* sp)
{
	int i;
	struct ng_mesg* rep = 0;
	struct ng_ksocket_accept* acp;
	char path[NG_PATHSIZ], hook[NG_PATHSIZ];
	struct ngm_connect conn;

	if (NgAllocRecvMsg(sp->cs, &rep, path) < 0)
	{
		perror("RecvMsg");
		return;
	}

	if ((rep->header.typecookie == NGM_KSOCKET_COOKIE) &&
			(rep->header.cmd == NGM_KSOCKET_ACCEPT))
	{
		acp = (struct ng_ksocket_accept*)rep->data;

		for (i = 0; i < NG_ONE2MANY_MAX_LINKS; i++)
			if (!one2manyconf.enabledLinks[i])
				break;
		if (i == NG_ONE2MANY_MAX_LINKS)
		{
			NgSendMsg(sp->cs, ".:", NGM_GENERIC_COOKIE, NGM_RMHOOK,
				path, sizeof(struct ngm_rmhook));
			printf("No free hooks\n");
		}
		else
		{
			one2manyconf.enabledLinks[i] = 1;
			strcpy(conn.path, path);
			strcpy(conn.ourhook, "right");
			strcpy(conn.peerhook, "inet/stream/tcp");
			sprintf(hook, "tee_man%d:", i);
			if (NgSendMsg(sp->cs, hook, NGM_GENERIC_COOKIE,
					NGM_CONNECT, &conn, sizeof(conn)) < 0)
				perror("SendMsg connect ksocket one2many over tee");

			if (NgSendMsg(sp->cs, "supdev:", NGM_ONE2MANY_COOKIE,
					NGM_ONE2MANY_SET_CONFIG, &one2manyconf, sizeof(one2manyconf)) < 0)
				perror("NgSendMsg one2many set config");
		}

		if ((NgSendMsg(sp->cs, "sockmast",
				NGM_KSOCKET_COOKIE, NGM_KSOCKET_ACCEPT, NULL, 0)) < 0)
			perror("ACCEPT");
	}
	free(rep);
}

void d_read_handler(struct sock_pair* sp)
{
	char hook[NG_HOOKSIZ], path[NG_HOOKSIZ];
	u_char* buf = 0;
	int len;
	int num;

	len = NgAllocRecvData(sp->ds, &buf, hook);
	if (!len && !strncmp(hook, "mann", 4))
	{
		sscanf(hook, "mann%d", &num);
		sprintf(path, "tee_man%d:", num);
		printf("Removing hook=%s\n", path);
		if (NgSendMsg(sp->cs, path, NGM_GENERIC_COOKIE, NGM_RMHOOK,
				"right", sizeof(struct ngm_rmhook)) < 0)
			perror("NgSendMsg NGM_RMHOOK");

		one2manyconf.enabledLinks[num] = 0;
		if (NgSendMsg(sp->cs, "supdev:", NGM_ONE2MANY_COOKIE,
				NGM_ONE2MANY_SET_CONFIG, &one2manyconf, sizeof(one2manyconf)) < 0)
			perror("NgSendMsg one2many set config");
	}

	free(buf);
}

anonymous
()
Ответ на: комментарий от ae1234

По порядку.

Насчет тупого цикла - верно. НО - этот цикл на выделенном ядре. Таких сегментов в решении крайне мало, что в моем случае это позволяет делать, и цели - латентность, этому способствуют. По ресурсам - не страшно, ибо сервер выделяется под достаточно простую задачку в плане аппаратных ресурсов - считаем их достаточными для этого :)

Насчет zero-copy. Сообщение включает в себя список сокетов и список аллоцированных через mmap буферов, это сообщение также аллоцировано уже через shared memory. Кстати - mmap или shmem - тут тоже вопрос :)

Вопрос не в общей латентности передачи - моя латентность заканчивается как только буферы передачи приняли сообщение. Все остальное в силу различных совершенно каналов клиентов я не могу никак прогнозировать, не то что за это отвечать :)

Повторюсь еще раз - если клиент переполнил буфер в 2 мегабайта, он для меня умер, high watermark тут в помощь :)

Если я что то не так понимаю, просветите, плиз :)

westtrd
() автор топика
typedef void (*kevhandler)(struct sock_pair*);

#define MY_TIMER 16

int main(int agc, char* agv[])
{
	struct sock_pair allinfo;
	int kq, i, inqueue, lis = 64;
	struct kevent ev[5];
	kevhandler evh;
	struct ngm_connect conn;
	struct ngm_mkpeer ngmkpeer;
	struct sockaddr_in sa;
	char buffer[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
	struct ng_mesg* resp = (struct ng_mesg*)&buffer[0];
	struct nodeinfo* ni = (struct nodeinfo*)resp->data;
	char hook[NG_HOOKSIZ], path[NG_HOOKSIZ];

	if (NgMkSockNode("suppup", &allinfo.cs, &allinfo.ds) < 0)
	{
		perror("NgMkSockNode");
		return (2);
	}

	strcpy(ngmkpeer.type, "tee");
	strcpy(ngmkpeer.ourhook, "pupsup");
	strcpy(ngmkpeer.peerhook, "right2left");
	if (NgSendMsg(allinfo.cs, ".:",
			NGM_GENERIC_COOKIE, NGM_MKPEER, &ngmkpeer, sizeof(ngmkpeer)) < 0)
	{
		perror("NgSendMsg tee create");
		return (2);
	}
	if (NgNameNode(allinfo.cs, ".:pupsup", "tee_nod"))
	{
		perror("NgNameNode tee");
		return (2);
	}

	strcpy(ngmkpeer.type, "one2many");
	strcpy(ngmkpeer.ourhook, "right");
	strcpy(ngmkpeer.peerhook, "one");
	if (NgSendMsg(allinfo.cs, "tee_nod:",
			NGM_GENERIC_COOKIE, NGM_MKPEER, &ngmkpeer, sizeof(ngmkpeer)) < 0)
	{
		perror("NgSendMsg one2many create");
		return (2);
	}

	if (NgNameNode(allinfo.cs, "tee_nod:right", "supdev"))
	{
		perror("NgNameNode one2many");
		return (2);
	}

	if (NgSendMsg(allinfo.cs, "supdev:", NGM_ONE2MANY_COOKIE,
			NGM_ONE2MANY_SET_CONFIG, &one2manyconf, sizeof(one2manyconf)) < 0)
	{
		perror("NgSendMsg one2many set config");
		return (2);
	}

	if (NgSendMsg(allinfo.cs, "tee_nod:right", NGM_GENERIC_COOKIE,
			NGM_NODEINFO, NULL, 0) < 0)
	{
		perror("NgSendMsg NGM_NODEINFO");
		return (2);
	}

	memset(buffer, 0, sizeof(buffer) / sizeof(buffer[0]));
	if (NgRecvMsg(allinfo.cs, resp,
		sizeof(buffer)/sizeof(buffer[0]), NULL) < 0)
	{
		perror("NgRecvMsg  NGM_NODEINFO");
		return (2);
	}

	for (i = 0; i < NG_ONE2MANY_MAX_LINKS; i++)
	{
		strcpy(ngmkpeer.type, "tee");
		sprintf(ngmkpeer.ourhook, "mann%d", i);
		strcpy(ngmkpeer.peerhook, "right2left");
		if (NgSendMsg(allinfo.cs, ".:", NGM_GENERIC_COOKIE,
				NGM_MKPEER, &ngmkpeer, sizeof(ngmkpeer)) < 0)
		{
			perror("NgSendMsg tee create");
			return (2);
		}
		sprintf(hook, "tee_man%d", i);
		strcat(strcpy(path, ".:"), ngmkpeer.ourhook);
		if (NgNameNode(allinfo.cs, path, hook))
		{
			perror("NgNameNode tee");
			return (2);
		}
		strcat(hook, ":");
		sprintf(conn.path, "[%x]:", ni->id);
		strcpy(conn.ourhook, "left");
		sprintf(conn.peerhook, "many%d", i);
		if (NgSendMsg(allinfo.cs, hook, NGM_GENERIC_COOKIE,
				NGM_CONNECT, &conn, sizeof(conn)) < 0)
		{
			perror("SendMsg connect tee");
			return (2);
		}
	}

	strcpy(ngmkpeer.type, "ksocket");
	strcpy(ngmkpeer.ourhook, "sockmast");
	strcpy(ngmkpeer.peerhook, "inet/stream/tcp");
	if (NgSendMsg(allinfo.cs, ".:",
			NGM_GENERIC_COOKIE, NGM_MKPEER, &ngmkpeer, sizeof(ngmkpeer)) < 0)
	{
		perror("NgSendMsg ksocket create");
		return (2);
	}
	sa.sin_len = sizeof(sa);
	sa.sin_port = htons(32747);
	sa.sin_family = PF_INET;
	sa.sin_addr.s_addr = INADDR_ANY;
	if ((NgSendMsg(allinfo.cs, ngmkpeer.ourhook,
			NGM_KSOCKET_COOKIE, NGM_KSOCKET_BIND, &sa, sizeof(sa))) < 0)
	{
		perror("NgSendMsg ksocket bind");
		return (2);
	}
	if ((NgSendMsg(allinfo.cs, ngmkpeer.ourhook,
			NGM_KSOCKET_COOKIE, NGM_KSOCKET_LISTEN, &lis, sizeof(lis))) < 0)
	{
		perror("NgSendMsg ksocket listen");
		return (2);
	}
	if ((NgSendMsg(allinfo.cs, ngmkpeer.ourhook,
			NGM_KSOCKET_COOKIE, NGM_KSOCKET_ACCEPT, NULL, 0)) < 0)
	{
		perror("NgSendMsg ksocket accept");
		return (2);
	}

anonymous
()
Ответ на: комментарий от anonymous

Вроде как я вел речь про запись в сокеты, но честно говоря, в код не смотрел глубоко :)

В любом случае спасибо, погляжу :)

westtrd
() автор топика
	if ((kq = kqueue()) < 0)
	{
		perror("kqueue");
		return (2);
	}

	EV_SET(&ev[0], allinfo.cs, EVFILT_READ, EV_ADD, 0, 0, (void*)c_read_handler);
	EV_SET(&ev[1], allinfo.ds, EVFILT_READ, EV_ADD, 0, 0, (void*)d_read_handler);
	EV_SET(&ev[2], MY_TIMER, EVFILT_TIMER, EV_ADD, 0, 5000, (void*)time_handler);
	kevent(kq, ev, 3, NULL, 0, NULL);
	while (1)
	{
		inqueue = kevent(kq, NULL, 0, ev, 5, NULL);
		for (i = 0; i < inqueue; i++)
		{
			evh = (kevhandler)ev[i].udata;
			evh(&allinfo);
		}
	}

	return (0);
}
anonymous
()
Ответ на: комментарий от anonymous

Спасибо, погляжу Если не затруднит, скиньте контакты на westfx гав мейл

westtrd
() автор топика
Ответ на: комментарий от westtrd

откомпили

CC -lnetgraph -o server server.c


Запусти от рута. Этот сервер слушает тебя на порту 32747 и каждые 5 секунд шлет клиенту время.
anonymous
()
Ответ на: комментарий от anonymous

а ничего что это только на freebsd ? :) и какая скорость будет ? за сколько времени 1000 клиентам оно разошлет по 128 байт ?

ae1234 ★★
()
Ответ на: комментарий от ae1234

И в других BSD есть нетграф. Под эти ваши линуксы есть коммерческий нетграф. И да, скорость через этот механизм будет максимальной, достаточной поглядеть ng_one2many.c. (Хотя оговорюсь, что в этом коммерческом порту нетграфа - неизвестно).

anonymous
()
Ответ на: комментарий от westtrd

я крайне смутно предствляю себе - зачем такой тупой цикл в раздатчике
или вы хотите - что при поступление задания - очень очень быстро оно начало раздаваться по клиентам ? с минимальной задержкой ?

насчет зеро-копи - еще раз - этот термин употребляеться когда обьем информации передают из сокета в сокет без задействования юзерспейса
а у вас список сокетов и так лежит в юзерспейсе - и ядерный модуль иль еще кто и так имеет до него доступ

не буферы передачи приняли - у вас сетевая сразу с сисколе уже пашлет пакет
и куда вы будете девать скажем 25мбайтный поток - когда до клиентов у вас только 10 мегобайт в секунду ?

ae1234 ★★
()
Ответ на: комментарий от anonymous

она не будет максимальной она не будет выше чем скорость отсылки из ядерного модуля спец для этого сделанного

ae1234 ★★
()
Ответ на: комментарий от ae1234

А ничего, что ng_one2many.ko - ядерный модуль, как раз для этого предназначенный, да еще и за много лет оттестированный?

anonymous
()
Ответ на: комментарий от ae1234

Удивляют безапеляционные заявления, при незнании предмета. (Нетграф - это ядерный фреймворк).

anonymous
()
Ответ на: комментарий от anonymous

а ты сделай тест - и результат покажи

удивляют безапелляционные заявления, при незнание результатов

ae1234 ★★
()
Ответ на: комментарий от ae1234

Глупо доказывать, что 2х2=4. Как это, «Общаясь с дураком, не оберешься сраму»(c). Ты ведь только что просил ядерный модуль, получил его, и на тебе - все равно не нравится. И сравнительные тесты с чем проводить?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.