0Pricing
Java Academy · บทเรียน

เมื่อการประมวลผลแบบขนานช่วยได้

ปัจจัยด้านปริมาณงานและขนาดข้อมูล

เมื่อการประมวลผลแบบขนานช่วยได้ เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

การทำงานแบบ parallel มีต้นทุน

การทำงานแบบ parallel เพิ่มต้นทุนแฝงจากการแบ่งข้อมูล การส่งงาน และการรวมผลลัพธ์ จะคุ้มค่าก็ต่อเมื่อต้นทุนนี้น้อยกว่าเวลาที่ประหยัดได้

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long sum = IntStream.rangeClosed(1, 10_000_000)
            .parallel()
            .asLongStream()
            .sum();
        System.out.println(sum);
    }
}

ปัจจัยที่ 1: ขนาดข้อมูล (N)

N ที่มีขนาดใหญ่ช่วยเฉลี่ยต้นทุนแฝงคงที่ของการทำงานแบบ parallel แนวทางคร่าว ๆ คือควรมีองค์ประกอบหลายหมื่นรายการก่อนที่การทำงานแบบ parallel จะคุ้มค่า

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long count = IntStream.rangeClosed(1, 5_000_000)
            .parallel()
            .filter(n -> n % 7 == 0)
            .count();
        System.out.println(count);
    }
}

ปัจจัยที่ 2: งานต่อองค์ประกอบ (Q)

ต้นทุน Q ของการประมวลผลแต่ละองค์ประกอบมีความสำคัญ งานต่อองค์ประกอบที่ใช้ทรัพยากรมาก เช่น การคำนวณหนัก จะได้ประโยชน์จากการทำงานแบบ parallel แม้ N จะมีขนาดเล็กกว่า

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long primes = IntStream.rangeClosed(2, 200_000)
            .parallel()
            .filter(Main::isPrime)
            .count();
        System.out.println(primes);
    }

    static boolean isPrime(int n) {
        for (int i = 2; (long) i * i <= n; i++)
            if (n % i == 0) return false;
        return true;
    }
}

แนวคิด N คูณ Q

ให้คิดว่างานที่มีประโยชน์ทั้งหมดคือ N x Q ยิ่งผลคูณนี้มีค่ามาก การทำงานแบบ parallel ก็ยิ่งช่วยได้มาก N ที่เล็กมากหรือ Q ที่แทบไม่มีงานมักไม่คุ้มที่จะใช้

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        double sum = IntStream.rangeClosed(1, 1_000_000)
            .parallel()
            .mapToDouble(n -> Math.sqrt(n) * Math.log(n + 1))
            .sum();
        System.out.println(sum);
    }
}

ปัจจัยที่ 3: ความสามารถในการแบ่ง

แหล่งข้อมูลที่แบ่งได้รวดเร็วและสม่ำเสมอจะเหมาะกับการทำงานแบบ parallel ได้แก่ อาร์เรย์ ArrayList และ IntStream.range ส่วน LinkedList และแหล่งข้อมูลที่ใช้ตัววนซ้ำจะแบ่งได้ไม่ดี

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1_000_000; i++) list.add(i);
        long even = list.parallelStream().filter(n -> n % 2 == 0).count();
        System.out.println(even);
    }
}

แหล่งข้อมูลที่ดี: อาร์เรย์และช่วง

ช่วงชนิดพื้นฐานมีขนาดที่ทราบแน่นอนและแบ่งได้ในเวลา O(1) จึงเป็นแหล่งข้อมูลแบบ parallel ที่เหมาะอย่างยิ่ง

import java.util.stream.LongStream;

public class Main {
    public static void main(String[] args) {
        long sum = LongStream.rangeClosed(1, 20_000_000)
            .parallel()
            .sum();
        System.out.println(sum);
    }
}

แหล่งข้อมูลที่ไม่ดี: ตัวสร้างค่าแบบวนซ้ำ

Stream.iterate สร้างองค์ประกอบตามลำดับโดยนิยาม จึงไม่สามารถแบ่งได้จนกว่าองค์ประกอบจะถูกสร้างขึ้นจริง ทำให้เป็นแหล่งข้อมูลแบบ parallel ที่ไม่เหมาะนัก

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        long count = Stream.iterate(1, n -> n + 1)
            .limit(1_000_000)
            .parallel()
            .filter(n -> n % 2 == 0)
            .count();
        System.out.println(count);
    }
}

ปัจจัยที่ 4: ขั้นตอนการรวมที่มีต้นทุนต่ำ

การทำงานแบบ parallel ต้องมีวิธีรวมผลลัพธ์บางส่วนที่ใช้ต้นทุนต่ำ sum และ count รวมกันได้ง่าย แต่การสร้างรายการที่เรียงลำดับหรือตัวรวบรวมที่ใช้โครงสร้างต้นไม้จำนวนมากจะมีต้นทุนการรวมสูง

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int max = IntStream.rangeClosed(1, 5_000_000)
            .parallel()
            .map(n -> n % 1000)
            .max()
            .getAsInt();
        System.out.println(max);
    }
}

หลีกเลี่ยงกับงานที่รออินพุต/เอาต์พุต

สตรีมแบบ parallel มุ่งเป้าไปที่งานที่ใช้ CPU เป็นหลักบนพูลแบบ fork-join การดำเนินการอินพุต/เอาต์พุตแบบบล็อกจะทำให้พูลร่วมไม่มีเธรดว่าง และส่งผลเสียต่อทั้งแอปพลิเคชัน ให้ใช้ตัวจัดการเธรดเฉพาะสำหรับอินพุต/เอาต์พุตแทน

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long total = IntStream.rangeClosed(1, 1_000_000)
            .parallel()
            .mapToLong(n -> (long) n * n)
            .sum();
        System.out.println(total);
    }
}

วัดผล อย่าคาดเดา

วิธีเดียวที่เชื่อถือได้ในการทราบว่าการทำงานแบบ parallel ช่วยได้หรือไม่ คือ ทดสอบประสิทธิภาพด้วยข้อมูลที่ใกล้เคียงการใช้งานจริง การคาดเดาประสิทธิภาพจากความรู้สึกมักผิดพลาด

import java.util.stream.LongStream;

public class Main {
    public static void main(String[] args) {
        long start = System.nanoTime();
        long sum = LongStream.rangeClosed(1, 50_000_000).parallel().sum();
        long ms = (System.nanoTime() - start) / 1_000_000;
        System.out.println("sum=" + sum + " took ~" + ms + "ms");
    }
}

รายการตรวจสอบที่ใช้ได้จริง

เลือกใช้การทำงานแบบ parallel เมื่อเป็นจริงทั้งหมด:

  • N มีขนาดใหญ่ และ/หรืองานต่อองค์ประกอบใช้เวลามาก
  • มีแหล่งข้อมูลที่แบ่งได้ เช่น อาร์เรย์, ArrayList หรือ range
  • มีการรวมที่มีต้นทุนต่ำและมีสมบัติการจัดหมู่
  • เป็นงานที่ใช้ CPU เป็นหลักและไม่มีอินพุต/เอาต์พุตแบบบล็อก
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        double avg = IntStream.rangeClosed(1, 10_000_000)
            .parallel()
            .mapToDouble(Math::sqrt)
            .average()
            .getAsDouble();
        System.out.println(avg);
    }
}

ตรวจสอบความเข้าใจ

สถานการณ์ใดเหมาะสมที่สุดสำหรับสตรีมแบบ parallel?

สรุปทบทวน

คุณได้เรียนรู้ว่าเมื่อใดการทำงานแบบ parallel จึงช่วยได้:

  • ประโยชน์เพิ่มขึ้นตาม N x Q (ขนาดข้อมูลคูณต้นทุนต่องค์ประกอบ)
  • ต้องมีแหล่งข้อมูลที่แบ่งได้ เช่น อาร์เรย์, ArrayList หรือช่วง
  • ต้องมีการรวมที่มีต้นทุนต่ำและมีสมบัติการจัดหมู่
  • ควรเป็นงานที่ใช้ CPU เป็นหลัก หลีกเลี่ยงอินพุต/เอาต์พุตแบบบล็อก และวัดผลเสมอ

คำถามที่พบบ่อย

บทเรียน “เมื่อการประมวลผลแบบขนานช่วยได้” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เมื่อการประมวลผลแบบขนานช่วยได้” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เมื่อการประมวลผลแบบขนานช่วยได้”

ปัจจัยด้านปริมาณงานและขนาดข้อมูล คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “เมื่อการประมวลผลแบบขนานช่วยได้” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การสร้างสตรีมแบบขนาน
  2. เมื่อการประมวลผลแบบขนานช่วยได้
  3. ความปลอดภัยของเธรดและผลข้างเคียง
  4. ข้อผิดพลาดที่พบบ่อย
← กลับไปที่ Java Academy