জাভা 8 স্ট্রিমস: একাধিক ফিল্টার বনাম জটিল শর্ত


235

কখনও কখনও আপনি Streamএকাধিক শর্ত দিয়ে একটি ফিল্টার করতে চান :

myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...

বা আপনি একটি জটিল শর্ত এবং একক সঙ্গে একই কাজ করতে পারে filter:

myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...

আমার অনুমান যে দ্বিতীয় পদ্ধতির আরও ভাল পারফরম্যান্স বৈশিষ্ট্য রয়েছে, তবে আমি এটি জানি না।

প্রথম পঠনযোগ্যতা পঠনযোগ্যতায় জিতেছে, তবে পারফরম্যান্সের জন্য আরও ভাল কী?


57
যে কোনও কোডটি পরিস্থিতিটিতে আরও পাঠযোগ্য Write পারফরম্যান্সের পার্থক্যটি ন্যূনতম (এবং অত্যন্ত পরিস্থিতিগত)।
ব্রায়ান গয়েটজ

5
ন্যানো-অপটিমাইজেশন সম্পর্কে ভুলে যান এবং অত্যন্ত পঠনযোগ্য এবং রক্ষণাবেক্ষণযোগ্য কোড ব্যবহার করুন। স্ট্রিম সহ, ফিল্টার সহ প্রতিটি অপারেশনকে সর্বদা আলাদাভাবে ব্যবহার করা উচিত।
ডায়াবলো

উত্তর:


150

উভয় বিকল্পের জন্য যে কোডটি কার্যকর করতে হবে তা এতটাই সমান যে আপনি নির্ভরযোগ্যভাবে কোনও ফলাফলের পূর্বাভাস দিতে পারবেন না। অন্তর্নিহিত অবজেক্টের কাঠামো আলাদা হতে পারে তবে হটস্পট অপ্টিমাইজারের পক্ষে এটি কোনও চ্যালেঞ্জ নয়। সুতরাং এটি পার্শ্ববর্তী অন্যান্য অবস্থার উপর নির্ভর করে যা কোনও তাত্পর্য থাকলে, দ্রুত মৃত্যুদন্ড কার্যকর করবে।

দুই ফিল্টার দৃষ্টান্ত মিশ্রন আরো বস্তু এবং অত: পর আরো প্রতিনিধিরূপে কোড তৈরি করে কিন্তু আপনি যদি ল্যামডা এক্সপ্রেশন বদলে পদ্ধতি রেফারেন্স ব্যবহার যেমন প্রতিস্থাপন এটিকে পরিবর্তন করতে পারেন filter(x -> x.isCool())দ্বারা filter(ItemType::isCool)। এইভাবে আপনি আপনার ল্যাম্বডা অভিব্যক্তির জন্য তৈরি সিন্থেটিক প্রতিনিধিত্ব পদ্ধতিটি মুছে ফেলেছেন। সুতরাং দুটি পদ্ধতির রেফারেন্স ব্যবহার করে দুটি ফিল্টার একত্রিত করা filterল্যাম্বডা এক্সপ্রেশনটির সাহায্যে একক অনুরোধের চেয়ে একই বা কম প্রতিনিধি কোড তৈরি করতে পারে &&

তবে, যেমনটি বলা হয়েছে, হটস্পট অপ্টিমাইজারের মাধ্যমে এই জাতীয় ওভারহেড দূর হবে এবং তা নগন্য।

তত্ত্ব অনুসারে, দুটি ফিল্টার একটি একক ফিল্টারের চেয়ে সহজ সমান্তরাল হতে পারে তবে এটি কেবল গণনীয় তীব্র কাজের জন্যই প্রাসঙ্গিক ¹

সুতরাং কোন সহজ উত্তর নেই।

নীচের লাইনটি, গন্ধ সনাক্তকরণ থ্রেশহোল্ডের নীচে যেমন পারফরম্যান্সের পার্থক্যগুলি সম্পর্কে চিন্তা করবেন না। আরও পাঠযোগ্য কি তা ব্যবহার করুন।


¹… এবং পরবর্তী পর্যায়ে সমান্তরাল প্রক্রিয়াকরণ করার একটি বাস্তবায়ন প্রয়োজন হবে, বর্তমানে স্ট্রিম স্ট্যান্ডার্ড বাস্তবায়নের দ্বারা গৃহীত একটি রাস্তা


4
কোডটি কি প্রতিটি ফিল্টারের পরে ফলাফল প্রবাহকে পুনরাবৃত্তি করতে পারে না?
jucardi

13
@ জুয়ান কার্লোস ডিয়াজ: না, স্ট্রিমগুলি সেভাবে কাজ করে না। "অলস মূল্যায়ন" সম্পর্কে পড়ুন; মধ্যবর্তী ক্রিয়াকলাপগুলি কিছুই করে না, তারা কেবলমাত্র টার্মিনাল অপারেশনের ফলাফলকে পরিবর্তন করে।
হোলার

33

একটি জটিল ফিল্টার শর্ত পারফরম্যান্স দৃষ্টিকোণে আরও ভাল, তবে সেরা পারফরম্যান্সটি একটি স্ট্যান্ডার্ড সহ লুপের জন্য পুরানো ফ্যাশন দেখায় if clauseসেরা বিকল্প। একটি ছোট অ্যারে 10 উপাদানের পার্থক্যের পার্থক্য ~ 2 বার হতে পারে, বড় অ্যারের জন্য পার্থক্যটি এত বড় নয়।
আপনি আমার গিটহাব প্রকল্পটি একবার দেখে নিতে পারেন , যেখানে আমি একাধিক অ্যারে পুনরুক্তি বিকল্পগুলির জন্য পারফরম্যান্স পরীক্ষা করেছি

ছোট অ্যারের জন্য 10 এলিমেন্ট থ্রুপুট অপ্স / গুলি: 10 উপাদান অ্যারে মাঝারি 10,000 উপাদানগুলির মাধ্যমে থ্রুটপুট অপস / এস: এখানে চিত্র বর্ণনা লিখুন বড় অ্যারের জন্য 1,000,000 উপাদান থ্রুপুট ওপ্স / গুলি: 1 এম উপাদান

দ্রষ্টব্য: পরীক্ষা চলছে

  • 8 সিপিইউ
  • 1 জিবি র‌্যাম
  • ওএস সংস্করণ: 16.04.1 এলটিএস (জেনিয়াল জেরাস)
  • জাভা সংস্করণ: 1.8.0_121
  • jvm: -XX: + UseG1GC -server -Xmx1024m -Xms1024m

হালনাগাদ: জাভা ১১ এর পারফরম্যান্সে কিছুটা অগ্রগতি রয়েছে তবে গতিশীলতা একইরকম থাকে

বেঞ্চমার্ক মোড: থ্রুপুট, অপ্স / সময় জাভা 8vs11


22

এই পরীক্ষাটি দেখায় যে আপনার দ্বিতীয় বিকল্পটি আরও ভালভাবে পারফর্ম করতে পারে। প্রথমে অনুসন্ধানগুলি, তারপরে কোড:

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());
}

3
আকর্ষণীয় - যখন আমি টেস্ট 1 এর আগে টেস্ট 2 চালানোর ক্রমটি পরিবর্তন করি তখন টেস্ট 1 কিছুটা ধীর গতিতে চলে। টেস্ট 1 প্রথম রান করলেই এটি দ্রুততর মনে হয়। যে কেউ এর পুনরুত্পাদন করতে পারে বা এর অন্তর্দৃষ্টি থাকতে পারে?
স্পেরার

5
এটি হতে পারে কারণ হটস্পট সংকলনের ব্যয়টি প্রথমে যা যা পরীক্ষা চালানো হয় তার দ্বারা ব্যয় হয়।
ডাবলিক

@ স্পার আপনি ঠিক বলেছেন, যখন অর্ডার পরিবর্তন হয়েছে, ফলাফল অনুমানযোগ্য নয়। তবে, আমি যখন এটি তিনটি ভিন্ন থ্রেড দিয়ে চালাই, সর্বদা জটিল ফিল্টারটি আরও ভাল ফলাফল দেয়, প্রথমে যে থ্রেডটি শুরু হয় নির্বিশেষে। নীচে ফলাফল রয়েছে। Test #1: {count=100, sum=7207, min=65, average=72.070000, max=91} Test #3: {count=100, sum=7959, min=72, average=79.590000, max=97} Test #2: {count=100, sum=8869, min=79, average=88.690000, max=110}
পরমেশ কোরাকাকুতি

2

এটি @ হ্যাঙ্ক ডি দ্বারা ভাগ করা নমুনা পরীক্ষার 6 টি বিভিন্ন সংমিশ্রণের ফলাফল এটি স্পষ্ট যে ফর্মের ভবিষ্যদ্বাণীটি u -> exp1 && exp2সমস্ত ক্ষেত্রেই অত্যন্ত পারফরম্যান্ট।

one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=3372, min=31, average=33.720000, max=47}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9150, min=85, average=91.500000, max=118}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9046, min=81, average=90.460000, max=150}

one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8336, min=77, average=83.360000, max=189}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9094, min=84, average=90.940000, max=176}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10501, min=99, average=105.010000, max=136}

two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=11117, min=98, average=111.170000, max=238}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8346, min=77, average=83.460000, max=113}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9089, min=81, average=90.890000, max=137}

two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10434, min=98, average=104.340000, max=132}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9113, min=81, average=91.130000, max=179}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8258, min=77, average=82.580000, max=100}

one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9131, min=81, average=91.310000, max=139}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10265, min=97, average=102.650000, max=131}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8442, min=77, average=84.420000, max=156}

one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8553, min=81, average=85.530000, max=125}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8219, min=77, average=82.190000, max=142}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10305, min=97, average=103.050000, max=132}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.