какой был бы наиболее эффективный способ сделать это?

#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. мой ответ — выход из цикла