Я хотел бы получить 0,5-1 000 удаленных вызовов функций в секунду. Предположим, у нас есть один компьютер Central
, на котором начинается вычисление, и один компьютер Worker
, который выполняет вычисления. В реальной конфигурации будет много компьютеров Worker.1 миллион удаленных вызовов функций в секунду
Давайте предположим на минуту, что наша задача заключается в расчете sum of [(random int from 0 to MAX_VAL)*2], PROBLEM_SIZE times
Очень наивная прототип
Worker:
//The real function takes 0.070ms to compute.
int compute(int input) {
return input * 2;
}
void go() {
try {
ServerSocket ss = new ServerSocket(socketNum);
Socket s = ss.accept();
System.out.println("Listening for " + socketNum);
DataInput di = new DataInputStream(s.getInputStream());
OutputStream os = s.getOutputStream();
byte[] arr = new byte[4];
ByteBuffer wrap = ByteBuffer.wrap(arr);
for (; ;) {
wrap.clear();
di.readFully(arr);
int value = wrap.getInt();
int output = compute(value);
wrap.clear();
byte[] bytes = wrap.putInt(output).array();
os.write(bytes);
}
} catch (IOException e) {
System.err.println("Exception at " + socketNum);
e.printStackTrace();
}
}
Central:
void go(){
try {
Socket s = new Socket(ip, socketNum);
s.setSoTimeout(2000);
OutputStream os = s.getOutputStream();
DataInput di = new DataInputStream(s.getInputStream());
System.out.println("Central socket starting for " + socketNum);
Random r = new Random();
byte[] buf = new byte[4];
ByteBuffer wrap = ByteBuffer.wrap(buf);
long start = System.currentTimeMillis();
long sum = 0;
for(int i = 0; i < n; i++) {
wrap.clear();
int value = r.nextInt(10000);
os.write(wrap.putInt(value).array());
di.readFully(buf);
wrap.clear();
int answer = wrap.getInt();
sum += answer;
}
System.out.println(n + " calls in " + (System.currentTimeMillis() - start) + " ms");
} catch(SocketTimeoutException ste) {
System.err.println("Socket timeout at " + socketNum);
}
catch (Exception e) {
e.printStackTrace();
}
Если пинг 0.150ms и мы запускаем 1-threaded Worker и 1-threaded Central, каждая итерация займет ~ 0.150ms. Чтобы повысить производительность, я запускаю потоки N
как на рабочем, так и на центральном, n
-й поток прослушивает порт 2000+n
. После завершения каждого потока мы суммируем результат.
Бенчмарки
Во-первых, я запустил программу выше в школьной сети моего стипендиата. Во-вторых, я запускал его на двух экземплярах Amazon EC2 Cluster. Разрыв в результатах был очень большим.
CHUNK_SIZE = 100_000
во всех пробегах. Сеть
стипендиата:
Я думаю, что 3 года назад это была конфигурация верхней доступна (Xeon E5645). Я считаю, что он сильно оптимизирован для параллельных вычислений и имеет простую топологию LAN, так как он имеет всего 20 машин.
ОС: Ubuntu
Средний пинг: ~ 0.165ms
N=1 total time=6 seconds
N=10 total time=9 seconds
N=20 total time=11 seconds
N=32 total time=14 seconds
N=100 total time=21 seconds
N=500 total time=54 seconds
Amazon сети:
Я запустил программу на два кластера Compute Eight Extra Large Instance (cc2.8xlarge) начали в той же Группе размещения.
ОС некоторых амазонских Linux
Средний пинг: ~ 0.170ms.
результаты были немного разочаровывает:
N=1 total time=16 seconds
N=10 total time=36 seconds
N=20 total time=55 seconds
N=32 total time=82 seconds
N=100 total time=250 seconds
N=500 total time=1200 seconds
Я побежал каждой конфигурации в 2-4 раза, результаты были сходными, в основном + -5%
Амазонка N = 1 результат имеет смысл, так как 0.170ms на вызов функции = 6000 вызовов в секунду = 100_000 звонков за 16 секунд. 6 секунд для сети Fellow на самом деле удивительны.
Я думаю, что максимальные TCP-пакеты в секунду с современными сетями составляют около 40-70 тыс. В секунду. Он соответствует N = 100, время = 250 секунд: N * CHUNK_SIZE/time = 100 * 100_000packets/250sec = 10_000_000packets/250sec = 40_000packets/second.
Вопрос:, как мне удалось настроить конфигурацию сети/компьютера моего коллеги, особенно с высокими значениями N?
Мое предположение: расточительно ставить каждый запрос 4 байта и ответ 4 байта на отдельный пакет, так как накладные расходы ~ 40 байт. Было бы разумно объединить все эти крошечные запросы, скажем, 0,010 мс и поместить их в один большой пакет, а затем перераспределить запросы на соответствующие сокеты. Возможно реализовать объединение на уровне приложений, но, похоже, что сеть/ОС Fellow настроена для этого.
Обновление: Я играл с java.net.Socket.setTcpNoDelay(), он ничего не менял.
Конечная цель: Я приближаю уравнение с миллионами переменных, используя очень большое дерево. В настоящее время дерево с 200_000 узлами помещается в ОЗУ. Однако меня интересует аппроксимация уравнения, которое требует дерева с миллионами узлов. Это потребует нескольких терабайт ОЗУ. Основная идея алгоритма - случайный путь от узла к листу, а также встроенные значения вдоль него. В настоящее время программа 32-потоковая, каждый поток выполняет 15000 итераций в секунду. Я хотел бы переместить его в кластер с одинаковыми номерами итераций в секунду.
Это не вопрос программирования, не так ли? –
Может быть, это (тогда, пожалуйста, дайте мне подсказку, где я должен искать ответ о конфигурации сети OS), или, может быть, текущий подход ошибочен, или я ДОЛЖЕН реализовать объединение, или есть готовое решение/инфраструктура для таких высокочастотных удаленные вызовы функций – 2013-02-22 14:40:35
Некоторые другие соображения включают в себя обеспечение количества потоков примерно равным числу процессоров и гарантируя, что конфликт блокировки отсутствует. – pamphlet