#java
#java
Вопрос:
float value = 0;
for (Foo foo : arrayList) {
float temporaryValue = calculate(foo);
if (temporaryValue < minValue) {
continue;
}
value = temporaryValue;
}
Можно ли оптимизировать производительность больше в Java 8?
Например, используя другой метод итерации или что-то еще.
Ответ №1:
Во-первых, упростил ваш последовательный код:
float value = 0;
for (Foo foo : arrayList) {
float temporaryValue = calculate(foo);
if (temporaryValue > minValue) {
value = temporaryValue;
}
}
Затем, если количество элементов в ArrayList достаточно велико, вы можете попытаться распараллелить свой код. Во-первых, сделайте ваш метод распараллеливаемым:
static void some_method(float[] values, int threadID, int total_threads){
float value = 0;
float minValue = Integer.MIN_VALUE;
for (int i = threadID; i < arrayList.size(); i = total_threads) {
float temporaryValue = calculate(arrayList.get(i));
if (temporaryValue < minValue) {
continue;
}
value = temporaryValue;
}
values[threadID] = value;
}
В приведенном выше коде каждый поток будет отвечать только за поиск максимального значения из фрагмента списка (т. е. for (int i = threadID; i < arrayList.size(); i = total_threads)
). В конце концов, каждый поток обновляет в соответствующей позиции массива значений (т.Е. values[ThreadId] = value;) максимальное значение, которое он нашел. Это обновление следует выполнить в конце, чтобы свести к минимуму возможные накладные расходы на совместное использование ложных данных.
Теперь создайте потоки, назначьте им работу и дождитесь их завершения.
int total_threads = 2;
float[] values = new float[total_threads];
List<Thread> threads = new ArrayList<>(total_threads);
for(int i = 0; i < total_threads; i ){
final int threadID = i;
threads.add(new Thread(() -> some_method(values, threadID, total_threads)));
}
threads.forEach(Thread::start);
threads.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// Calculate the maximum
float max_value = Integer.MIN_VALUE;
for (float v : values) {
max_value = Math.max(v, max_value);
}
}
После того, как каждый поток выполнил свою работу и нашел свое максимальное значение, основной поток найдет из этих значений, какое из них является самым большим:
float max_value = Integer.MIN_VALUE;
for (float v : values) {
max_value = Math.max(v, max_value);
}
Возможно, вы захотите дополнительно настроить распределение итераций по потоку:
for (int i = threadID; i < arrayList.size(); i = total_threads)
в зависимости от содержимого calculate
метода может окупиться использование динамического распределения циклов.
Ответ №2:
Поскольку последнее значение в списке, удовлетворяющее критериям, «выигрывает», вы можете выполнить итерацию в обратном направлении и выйти из цикла, как только первое значение будет больше или равно минимальному значению. Предполагая, что метод calculate не имеет важных побочных эффектов.
Ответ №3:
Удалите лишние шаги:
if(temporaryValue > value) {
value = temporaryValue;
}
Или просто break
из цикла, когда у вас есть значение>= minValue
.
В качестве альтернативы используйте потоки Java 8:
final float value = (float) arrayList
.stream()
.mapToDouble(this::calculate)
.max();
Это вряд ли улучшит производительность по сравнению с базовым циклом, на самом деле это, вероятно, медленнее. Однако потоки можно легко распараллелить (или, по крайней мере, значительно проще, чем пользовательская многопоточность).
Но первый вопрос, который вам нужно задать, — нужно ли мне оптимизировать этот код? Насколько велик ваш список? Как часто вызывается этот код? Насколько сложен calculate()
метод по сравнению с итерацией?
Комментарии:
1. Превращение простого цикла for в поток, как известно, не улучшает производительность. Если я верю тому, что слышал, это приводит к небольшим накладным расходам. Хотя может помочь распараллеливание.
2. метод calculate довольно большой, примерно в 80 строках.
3. @Aaron Хорошая мысль, я внесу соответствующие изменения.
4. В этом случае, возможно, метод calculate является лучшим местом для оптимизации. Хотя Streams API может упростить код и сделать его более читаемым, я был бы удивлен, если вы увидите какой-либо прирост производительности от этого. В зависимости от количества элементов в вашем списке может помочь многопоточность, но это приводит к накладным расходам, которые могут фактически увеличить время обработки небольшого списка.
5. мой ответ — выход из цикла