В базе есть миллион клиентов, для каждого клиента нужно сгенерировать отчет и сохранить обратно в базу.
Сейчас на эту задачу уходит 5 часов если выполнять последовательно.
Попробовал через пул-потокв (10 потоков) — заняло 4,5 часов.
Согласно бизнес требованиям задача должна выполнятся не более 1 часа.
Приложение использует Spring + Hibernate и одну базу данных. Потяно что потоки конкурируют за доступ к данным, но все равно мне непонятно почему 10 потоков не дали значительного прироста производительности.
Здравствуйте, Аноним, Вы писали:
А>В базе есть миллион клиентов, для каждого клиента нужно сгенерировать отчет и сохранить обратно в базу. А>Сейчас на эту задачу уходит 5 часов если выполнять последовательно. А>Попробовал через пул-потокв (10 потоков) — заняло 4,5 часов. А>Согласно бизнес требованиям задача должна выполнятся не более 1 часа.
Таки поставить профайлер и посмотреть на какие этапы уходить больше процессорного времени.
А>Приложение использует Spring + Hibernate и одну базу данных. Потяно что потоки конкурируют за доступ к данным,
Потоки конкурируют на чтение??? Надеюсь вы отчеты без Hibernate строите?
А>но все равно мне непонятно почему 10 потоков не дали значительного прироста производительности.
Возможно потому что узкое место не в CPU?
Здравствуйте, Аноним, Вы писали:
А>В базе есть миллион клиентов, для каждого клиента нужно сгенерировать отчет и сохранить обратно в базу. А>Сейчас на эту задачу уходит 5 часов если выполнять последовательно. А>Попробовал через пул-потокв (10 потоков) — заняло 4,5 часов. А>Согласно бизнес требованиям задача должна выполнятся не более 1 часа.
А>Приложение использует Spring + Hibernate и одну базу данных. Потяно что потоки конкурируют за доступ к данным, но все равно мне непонятно почему 10 потоков не дали значительного прироста производительности.
1. надеюсь в базе данных чтение и запись идет по разным таблицам
2. пул потоков соответсвует пулу конекшкнов к базе
3. для операций чтения транзакции задекларированы как read-only
4. правильно настроено/отключено кеширование в Hibernate
5. если модель данных не слишком сложная, имеет смысл отказаться от Hibernate в пользу JDBC
к пунктам 4-5 следует переходить после тщательного профилирования
Здравствуйте, Аноним, Вы писали:
А>В базе есть миллион клиентов, для каждого клиента нужно сгенерировать отчет и сохранить обратно в базу. А>Сейчас на эту задачу уходит 5 часов если выполнять последовательно. А>Попробовал через пул-потокв (10 потоков) — заняло 4,5 часов. А>Согласно бизнес требованиям задача должна выполнятся не более 1 часа.
А>Приложение использует Spring + Hibernate и одну базу данных. Потяно что потоки конкурируют за доступ к данным, но все равно мне непонятно почему 10 потоков не дали значительного прироста производительности.
Здравствуйте, Безон, Вы писали:
Б>А почему должны были дать?
А почему нет? Без узких мест распареллеливание даже на одноядерном процессоре даст видимый прирост.
Плюс еще можно попробовать подгружать данные не одного клиента за запрос, а сразу пачку, и затем раздавать обработчикам.
Дальше, посмотреть, как грузятся отчеты — возможно, стоит также нагенерить пачку и потом каким-нибудь BULK UNSERT.
Кроме того, использовать кеширование при генерации отчета — если есть общий кусок отчета для какой-то группы клиентов,
просто повторно его использовать, а не генерить каждый раз.
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, Безон, Вы писали:
Б>>А почему должны были дать? B>А почему нет? Без узких мест распареллеливание даже на одноядерном процессоре даст видимый прирост.
В Java concurrency on practice написанно как расчитывать оптимвальное кол-во потоков.
Никто не даст ссылку на эту статью? Или как вы считаете?
Здравствуйте, Аноним, Вы писали:
А>В Java concurrency on practice написанно как расчитывать оптимвальное кол-во потоков. А>Никто не даст ссылку на эту статью? Или как вы считаете?
Это?
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, Аноним, Вы писали:
А>>В Java concurrency on practice написанно как расчитывать оптимвальное кол-во потоков. А>>Никто не даст ссылку на эту статью? Или как вы считаете? B>Это? B>
А w- это время ожидания чего?
и я что-то не понимаю, что такое U_cpu?
Здравствуйте, unkis, Вы писали:
U>А w- это время ожидания чего?
Зачастую IO операций. В данном случае если БД находится на другом сервере, то всё время ожизания отклика на запросы.
U>и я что-то не понимаю, что такое U_cpu?
На сколько % мы собираемся загрузить процесоры.
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, Безон, Вы писали:
Б>>А почему должны были дать? B>А почему нет? Без узких мест распареллеливание даже на одноядерном процессоре даст видимый прирост.
А узкие места это что?
ЗЫ. переключение контекста не фига не бесплатно, поэтому бездумное распараллеливание легко просаживает производительность.
Здравствуйте, Безон, Вы писали:
Б>А узкие места это что?
Которые многопоточностью все равно не разрулить, так как они тормозят в любом случае.
Б>ЗЫ. переключение контекста не фига не бесплатно, поэтому бездумное распараллеливание легко просаживает производительность.
На 10 потоках и 2х ядрах можно считать что бесплатное.
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, Безон, Вы писали:
Б>>А узкие места это что? B>Которые многопоточностью все равно не разрулить, так как они тормозят в любом случае.
многопоточностью (в число потоков больше чем число ядер) разгоняется только код большую часть времени находящийся в ожидании данных. Точно так же он разгоняется событийно-управляемым кодом Б>>ЗЫ. переключение контекста не фига не бесплатно, поэтому бездумное распараллеливание легко просаживает производительность. B>На 10 потоках и 2х ядрах можно считать что бесплатное.
ну ну блажен кто верует ... Советую написать простой тест без ожидания ввода-вывода
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, Аноним, Вы писали:
А>>В Java concurrency on practice написанно как расчитывать оптимвальное кол-во потоков. А>>Никто не даст ссылку на эту статью? Или как вы считаете? B>Это? B>
На практике эта формула не работает, т.к. время ожидания обычно зависит от количества потоков. Представте, что 100 потоков делают одновременный запрос в БД. В этом случае время ожидания (грубо, время пока БД выполнит чтение данных с диска) будет больше чем, когда один поток делает один запрос.
А>Приложение использует Spring + Hibernate и одну базу данных. Потяно что потоки конкурируют за доступ к данным, но все равно мне непонятно почему 10 потоков не дали значительного прироста производительности.
А>Какие еще могут быть варианты.
может потому что генерация отчета ощутимо кушает cpu?
что если разбить задачу на несколько jobs, где будет обрабатываться определенное кол-во клиентов?
попробуй GridGain (www.gridgain.comwww.gridgain.com):
— пишешь свой task для разбиения на jobs
— запускаешь несколько машин с запущенным GridGain
— jobs автоматически раскидываются по всем grid nodes
Здравствуйте, Безон, Вы писали:
B>>Которые многопоточностью все равно не разрулить, так как они тормозят в любом случае. Б>многопоточностью (в число потоков больше чем число ядер) разгоняется только код большую часть времени находящийся в ожидании данных. Точно так же он разгоняется событийно-управляемым кодом
Напоминаю что разговор идет о создании отчетов для миллиона клиентов. Есть подозрения что отчеты создаются не одним запросом.
Б>>>ЗЫ. переключение контекста не фига не бесплатно, поэтому бездумное распараллеливание легко просаживает производительность. B>>На 10 потоках и 2х ядрах можно считать что бесплатное. Б>ну ну блажен кто верует ... Советую написать простой тест без ожидания ввода-вывода Написал. Убедился в своей правоте. Доказывать свою точку зрения на сферическом коне в вакууме можешь сколь угодно долго. Что будет следующим аргументом? Пример где в цикле вызывается Thread.yield()?
Здравствуйте, Blazkowicz, Вы писали:
Б>>>>ЗЫ. переключение контекста не фига не бесплатно, поэтому бездумное распараллеливание легко просаживает производительность. B>>>На 10 потоках и 2х ядрах можно считать что бесплатное. Б>>ну ну блажен кто верует ... Советую написать простой тест без ожидания ввода-вывода B> Написал. Убедился в своей правоте. Доказывать свою точку зрения на сферическом коне в вакууме можешь сколь угодно долго. Что будет следующим аргументом? Пример где в цикле вызывается Thread.yield()?
Написал маленький тест, он показывает, что примерно за одно и то же время при двух потоках успевает выполниться в среднем 78626 итераций, при десяти в среднем 76754 (проверял при четырех подходах на каждый из вариантов (2-10 потоков), amd 64 x2 core dual, os vista 32, jvm 1.6.0_03). При этом время потокам раздается примерно равномерно.
Итого разница составила около двух с половиной процентов, что несколько удвило, т.к. переключение контекста субъективно считалось мною более дорогой операцией. Понятно, что в тесте присутствует достаточно большая погрешность, но тем не менее тенденция налицо.
Если у интересующихся будет желание изменить тест/предложить альтернативный тест, показывающий ощутимо бОльшую разницу из-за переключения конекстов, милости просим, интересно обсудить.
package com;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class AAA {
private static final long DURATION = TimeUnit.NANOSECONDS.convert(30, TimeUnit.SECONDS);
private static final int THREADS_NUMBER = 50;
private static final ThreadLocal<AtomicLong> COUNTER = new ThreadLocal<AtomicLong>() {
@Override
protected AtomicLong initialValue() {
return new AtomicLong();
}
};
private static final ThreadLocal<AtomicLong> DUMMY_DATA = new ThreadLocal<AtomicLong>() {
@Override
protected AtomicLong initialValue() {
return new AtomicLong();
}
};
private static final AtomicLong DUMMY_COUNTER = new AtomicLong();
private static final AtomicLong END_TIME = new AtomicLong(System.nanoTime() + DURATION);
private static final List<ThreadLocal<CharSequence>> DUMMY_SOURCE = new ArrayList<ThreadLocal<CharSequence>>();
static {
for (int i = 0; i < 40; ++i) {
DUMMY_SOURCE.add(new ThreadLocal<CharSequence>());
}
}
private static final Set<Long> COUNTERS = new ConcurrentSkipListSet<Long>();
public static void main(String[] args) throws Exception {
final CountDownLatch startLatch = new CountDownLatch(THREADS_NUMBER);
final CountDownLatch endLatch = new CountDownLatch(THREADS_NUMBER);
for (int i = 0; i < THREADS_NUMBER; i++) {
new Thread() {
@Override
public void run() {
initDummyData();
startLatch.countDown();
try {
startLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
while (System.nanoTime() < END_TIME.get()) {
doJob();
}
COUNTERS.add(COUNTER.get().get());
DUMMY_COUNTER.addAndGet(DUMMY_DATA.get().get());
endLatch.countDown();
}
}.start();
}
startLatch.await();
END_TIME.set(System.nanoTime() + DURATION);
endLatch.await();
printStatistics();
}
private static void initDummyData() {
for (ThreadLocal<CharSequence> threadLocal : DUMMY_SOURCE) {
threadLocal.set(getRandomString());
}
}
private static CharSequence getRandomString() {
StringBuilder result = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 137; ++i) {
result.append((char)random.nextInt(0xFF));
}
return result;
}
private static void doJob() {
Random random = new Random();
for (ThreadLocal<CharSequence> threadLocal : DUMMY_SOURCE) {
for (int i = 0; i < threadLocal.get().length(); ++i) {
DUMMY_DATA.get().addAndGet(threadLocal.get().charAt(i) << random.nextInt(31));
}
}
COUNTER.get().incrementAndGet();
}
private static void printStatistics() {
long total = 0L;
for (Long counter : COUNTERS) {
total += counter;
}
System.out.printf("Total iterations number: %d, dummy data: %d, distribution:", total, DUMMY_COUNTER.get());
for (Long counter : COUNTERS) {
System.out.printf(" %f%%", counter * 100d / total);
}
}
}
Здравствуйте, denis.zhdanov, Вы писали:
DZ>Итого разница составила около двух с половиной процентов, что несколько удвило, т.к. переключение контекста субъективно считалось мною более дорогой операцией. Понятно, что в тесте присутствует достаточно большая погрешность, но тем не менее тенденция налицо.
DZ>Если у интересующихся будет желание изменить тест/предложить альтернативный тест, показывающий ощутимо бОльшую разницу из-за переключения конекстов, милости просим, интересно обсудить.
[/java]
У меня 10 потоков выдает бОльший результат, чем 4. 4-ядерный проц, jre 1.6.0_14
Здравствуйте, Аноним, Вы писали:
А>У меня 10 потоков выдает бОльший результат, чем 4. 4-ядерный проц,
Потому что на 4х ядрах 10 потоков работают быстрее чем на 2х.
Здравствуйте, denis.zhdanov, Вы писали:
DZ>код теста
Тест замечательный. И действительно показывает, что при полной изоляции данных для алгоритмов, параллельное их выполнение не ведет к сильному просаживанию производительности. Правда немного смущает использование Atomic при гарантированно однопоточном доступе.
Однако когда алгоритмы работают над разделяемыми данными, а тем более их модифицируют, вот тогда производительность упадет значительно больше. Насколько больше сказать не могу пока не будет теста.