Производительность фильтрации списка в java 8 [duplicate]

Для тех, кто находит это в будущем, я бы не рекомендовал использовать mail. Есть несколько ответов, которые касаются этого, но не из-за этого.

Функция PHP mail не только непрозрачна, но и полностью зависит от того, какой MTA вы используете (то есть Sendmail) для выполнения этой работы. mail будет ТОЛЬКО сообщать вам, не удалось ли MTA принять его (т. е. Sendmail был отключен, когда вы пытались отправить). Он не может сказать вам, была ли почта успешной, потому что она была передана. Как таковой (как детали ответов Джона Конде), вы теперь можете возиться с журналами MTA и надеяться, что он расскажет вам об отсутствии возможности исправить его. Если вы находитесь на общем хосте или не имеете доступа к журналам MTA, вам не повезло. К сожалению, по умолчанию для большинства ванильных инсталляций для Linux обрабатывается так.

Почтовая библиотека ( PHPMailer , Zend Framework 2+ и т. д.) делает что-то очень отличное от mail. То, что они делают, это открыть сокет непосредственно на принимающем почтовом сервере, а затем отправить SMTP-почтовые команды непосредственно через этот сокет. Другими словами, класс действует как собственный MTA (обратите внимание, что вы можете сказать библиотекам использовать mail, чтобы в конечном итоге отправить почту, но я настоятельно рекомендую вам не делать этого).

Что это означает, что вы можете непосредственно видеть ответы с принимающего сервера (например, в PHPMailer вы можете включить вывод отладки ). Больше не гадать, если почта не была отправлена ​​или почему.

Если вы используете SMTP (то есть вы вызываете isSMTP()), вы можете получить подробную расшифровку протокола SMTP, используя свойство SMTPDebug.

Установите этот параметр, включив в свой скрипт такую ​​строку:

$mail->SMTPDebug = 2;

Вы также получаете преимущество лучшего интерфейса. С помощью mail вы должны настроить все свои заголовки, вложения и т. Д. С библиотекой у вас есть специальная функция для этого. Это также означает, что функция выполняет все сложные элементы (например, заголовки).

131
задан Holger 10 May 2016 в 10:56
поделиться

2 ответа

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

Объединение двух экземпляров фильтра создает больше объектов и, следовательно, больше делегирует код, но это может измениться, если вы используете ссылки на методы, а не на какие-либо различия. чем лямбда-выражения, например замените filter(x -> x.isCool()) на filter(ItemType::isCool). Таким образом, вы устранили синтетический метод делегирования, созданный для выражения лямбды. Таким образом, объединение двух фильтров с использованием двух ссылок на методы может создать один и тот же или меньший код делегирования, чем один вызов filter, используя выражение лямбда с &&.

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

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

Таким образом, нет простой ответ.

Суть в том, что не стоит думать о таких различиях производительности ниже порога обнаружения запаха. Используйте то, что более читаемо.


¹ ... и потребуется реализация, выполняющая параллельную обработку последующих этапов, путь, который в настоящее время не выполняется стандартной реализацией потока

91
ответ дан Holger 21 August 2018 в 22:45
поделиться
  • 1
    не должен ли код перебирать результирующий поток после каждого фильтра? – Juan Carlos Diaz 21 November 2016 в 00:47
  • 2
    @Juan Карлос Диас: нет, потоки не работают таким образом. Читайте о «ленивой оценке»; промежуточные операции ничего не делают, они только изменяют результат операции терминала. – Holger 21 November 2016 в 10:24

Этот тест показывает, что ваш второй вариант может работать значительно лучше. Сначала выведите код:

one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=4142, min=29, average=41.420000, max=82}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=13315, min=117, average=133.150000, max=153}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10320, min=82, average=103.200000, max=127}

теперь код:

enum Gender {
    FEMALE,
    MALE
}

static class User {
    Gender gender;
    int age;

    public User(Gender gender, int age){
        this.gender = gender;
        this.age = age;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

static long test1(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
            .filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0)
            .allMatch(u -> true);                   // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
}

static long test2(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
            .filter(u -> u.getGender() == Gender.FEMALE)
            .filter(u -> u.getAge() % 2 == 0)
            .allMatch(u -> true);                   // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
}

static long test3(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
            .filter(((Predicate<User>) u -> u.getGender() == Gender.FEMALE).and(u -> u.getAge() % 2 == 0))
            .allMatch(u -> true);                   // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
}

public static void main(String... args) {
    int size = 10000000;
    List<User> users =
    IntStream.range(0,size)
            .mapToObj(i -> i % 2 == 0 ? new User(Gender.MALE, i % 100) : new User(Gender.FEMALE, i % 100))
            .collect(Collectors.toCollection(()->new ArrayList<>(size)));
    repeat("one filter with predicate of form u -> exp1 && exp2", users, Temp::test1, 100);
    repeat("two filters with predicates of form u -> exp1", users, Temp::test2, 100);
    repeat("one filter with predicate of form predOne.and(pred2)", users, Temp::test3, 100);
}

private static void repeat(String name, List<User> users, ToLongFunction<List<User>> test, int iterations) {
    System.out.println(name + ", list size " + users.size() + ", averaged over " + iterations + " runs: " + IntStream.range(0, iterations)
            .mapToLong(i -> test.applyAsLong(users))
            .summaryStatistics());
}
18
ответ дан Hank D 21 August 2018 в 22:45
поделиться
  • 1
    Интересно - когда я меняю порядок запуска test2 перед испытанием1, test1 работает немного медленнее. Это происходит только тогда, когда test1 запускается первым, что кажется быстрее. Может ли кто-нибудь воспроизвести это или иметь какие-либо идеи? – Sperr 4 August 2017 в 20:25
Другие вопросы по тегам:

Похожие вопросы: