Commit 1519a6db authored by lubna.alhalabi's avatar lubna.alhalabi

HW2

parents
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
\ No newline at end of file
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
Concurrency Model: Different classes use different concurrency models, such as threads, runnables, callables, and executor services. Each has its own advantages and trade-offs.
___
Threads: Threads are lightweight units of execution that share the same memory space, They are managed by the operating system or a runtime environment, Threads within a process share data and resources, which requires careful synchronization.
Pros:
-Efficient for parallelizing tasks within a single process.
-Shared memory allows easy communication between threads.
Cons:
-Synchronization can introduce complexities and potential issues like race conditions and deadlocks.
___
Runnable/Callable: Runnables and Callables are interfaces in Java that represent tasks that can be executed concurrently.
-Runnable is a simple interface for a task without a return value, while Callable allows a task to return a result and throw exceptions.
Pros:
-Easier to implement than managing threads directly.
-Suitable for dividing tasks into smaller units of work.
Cons:
-Limited control over the execution of tasks compared to managing threads directly.
___
Executor Service: An abstraction for managing and controlling the execution of tasks.Provides a higher-level interface for task submission, allowing better control over thread management.
Pros:
-Simplifies the management of threads and tasks.
-Allows the use of thread pools for efficient task reuse.
Cons:
-May introduce additional overhead compared to managing threads directly.
___
Task Partitioning: The worker and partitioner classes handle how the range of numbers is divided among different threads or tasks for parallel processing.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Lubna</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package data;
public class TestData {
public static int[] createData(int target) {
int[] data = new int[target];
for (int i = 0; i < data.length; i++) {
data[i] = i;
}
return data;
}
}
package org.example;
import data.TestData;
import parallelsummers.ParallelSummer0;
import parallelsummers.ParallelSummer1;
import parallelsummers.ParallelSummer2;
import parallelsummers.ParallelSummer3;
public class Main {
public static void main(String[] args) {
int size = 1000000;
int[] data = TestData.createData(size);
long result = SequentialSummer.sum(data);
int threadCount = 8;
System.out.println("Sequential summer:" + result);
System.out.println("Parallel summer 1: " + ParallelSummer0.sum(data, threadCount));
System.out.println("Parallel summer 2: " + ParallelSummer1.sum(data, threadCount));
System.out.println("Parallel summer 3: " + ParallelSummer2.sum(data, threadCount));
System.out.println("Parallel summer 4: " + ParallelSummer3.sum(data, threadCount));
}
}
\ No newline at end of file
package org.example;
public class SequentialSummer {
public static long sum(int[] data) {
long sum = 0;
for (int i = 1; i < data.length; i++) {
if (isPrime(i)) {
sum ++;
}
}
return sum;
}
// Private method to check if a number is prime
private static boolean isPrime(int num) {
// Check if the number is less than or equal to 1 (not prime)
if (num <= 1) {
return false;
}
// Check for some small prime numbers
if (num <= 3) {
return true;
}
// Check divisibility by 2 or 3
if (num % 2 == 0 || num % 3 == 0) {
return false;
}
// Check divisibility by numbers of the form 6k ± 1
for (int i = 5; i * i <= num; i += 6) {
if (num % i == 0 || num % (i + 2) == 0) {
return false;
}
}
return true;
}
}
package parallelsummers;
import summers.SummerThread;
import worker.WorkPartitioner;
import java.util.ArrayList;
import java.util.List;
public class ParallelSummer0{
public static long sum(int[] data, int threadCount) {
// List to store instances of SummerThread for parallel processing
List<SummerThread> summers = new ArrayList<>();
// List to store work partitions for each thread
List<WorkPartitioner.Part> parts = WorkPartitioner.partitions(data.length, threadCount);
// Create SummerThread instances for each work partition
for (WorkPartitioner.Part part : parts) {
summers.add(new SummerThread(data,part));
}
// Start each SummerThread to run in parallel
for (SummerThread summerThread : summers) {
summerThread.start();
}
// Wait for each thread to complete its execution
for (SummerThread summerThread : summers) {
try {
summerThread.join();
} catch (InterruptedException e) {
System.err.println("Thread cannot join!");
}
}
// Aggregate the results from each thread
int sum = 0;
for (SummerThread summerThread : summers) {
sum += summerThread.getSum();
}
// Return the final sum calculated in parallel
return sum;
}
}
package parallelsummers;
import summers.SummerRunnable;
import worker.WorkPartitioner;
import java.util.ArrayList;
import java.util.List;
public class ParallelSummer1 {
public static long sum(int[] data, int threadCount) {
// List to store instances of SummerRunnable for parallel processing
List<SummerRunnable> summers = new ArrayList<>();
// List to store work partitions for each thread
List<WorkPartitioner.Part> parts = WorkPartitioner.partitions(data.length, threadCount);
// Create SummerRunnable instances for each work partition
for (WorkPartitioner.Part part : parts) {
summers.add(new SummerRunnable(data,part));
}
// Start each SummerRunnable to run in parallel
for (SummerRunnable SummerRunnable : summers) {
SummerRunnable.startThread();
}
// Wait for each thread to complete its execution
for (SummerRunnable SummerRunnable : summers) {
try {
SummerRunnable.joinThread();
} catch (InterruptedException e) {
System.err.println("Thread cannot join!");
}
}
// Aggregate the results from each thread
long sum = 0;
for (SummerRunnable summerRunnable : summers) {
sum += summerRunnable.getSum();
}
// Return the final sum calculated in parallel
return sum;
}
}
\ No newline at end of file
package parallelsummers;
import summers.SummerThread;
import worker.WorkPartitioner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ParallelSummer2{
public static long sum(int[] data, int threadCount) {
// List to store instances of SummerThread for parallel processing
List<SummerThread> summers = new ArrayList<>();
// List to store work partitions for each thread
List<WorkPartitioner.Part> parts = WorkPartitioner.partitions(data.length, threadCount);
// Create SummerThread instances for each work partition
for (WorkPartitioner.Part part : parts) {
summers.add(new SummerThread(data,part));
}
// Create a fixed thread pool with the specified thread count
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
// Submit each SummerThread to the executor for parallel execution
for (SummerThread summer: summers) {
executor.execute(summer);
}
// Shutdown the executor to no longer accept new tasks
executor.shutdown();
// Wait for all submitted tasks to complete
while (!executor.isTerminated()) {
}
// Calculate the total sum by summing up individual thread sums
return summers.stream().mapToLong(SummerThread::getSum).sum();
}
}
package parallelsummers;
import summers.SummerCallable;
import worker.WorkPartitioner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ParallelSummer3 {
public static long sum(int[] data, int threadCount) {
// Create a fixed thread pool with the specified thread count
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
// List to store instances of SummerCallable for parallel processing
List<SummerCallable> summers = new ArrayList<SummerCallable>();
// List to store work partitions for each thread
List<WorkPartitioner.Part> parts = WorkPartitioner.partitions(data.length, threadCount);
// Create SummerCallable instances for each work partition
for (WorkPartitioner.Part part : parts) {
summers.add(new SummerCallable(data,part));
}
List<Future<Long>> results;
// Invoke all SummerCallable tasks and store the Future objects
try {
results = executor.invokeAll(summers);
} catch (InterruptedException e) {
System.err.println("Cannot invoke the threads.");
return -1;
}
// Shutdown the executor to no longer accept new tasks
executor.shutdown();
// Calculate the total sum by summing up individual thread sums
long sum = 0;
for (Future<Long> future : results) {
try {
// Retrieve and add the result from each thread
sum += future.get();
} catch (InterruptedException | ExecutionException e) {
System.err.println("Cannot get the results from threads.");
return -2;
}
}
// Return the final sum calculated in parallel
return sum;
}
}
\ No newline at end of file
package parallelsummers;
import summers.SummerCallable;
import worker.WorkPartitioner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ParallelSummer4{
public static long sum(int[] data, int threadCount) {
// Create a fixed thread pool with the specified thread count
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
// List to store Future objects for parallel processing
List<Future<Long>> results = new ArrayList<Future<Long>>();
// List to store work partitions for each thread
List<WorkPartitioner.Part> parts = WorkPartitioner.partitions(data.length, threadCount);
// Submit SummerCallable tasks to the executor and store the Future objects
for (WorkPartitioner.Part part : parts) {
results.add(executor.submit(new SummerCallable(data, part)));
}
// Shutdown the executor to no longer accept new tasks
executor.shutdown();
// Wait for all submitted tasks to complete
while (!executor.isTerminated()) {
}
// Calculate the total sum by summing up individual thread sums
long sum = 0;
for (Future<Long> future : results) {
try {
// Retrieve and add the result from each thread
sum += future.get();
} catch (InterruptedException | ExecutionException e) {
System.err.println("Cannot get the results from threads.");
return -2;
}
}
// Return the final sum calculated in parallel
return sum;
}
}
package summers;
import worker.WorkPartitioner;
import worker.Worker;
public class Summer extends Worker {
// Variable to store the sum
protected long sum;
// Constructor that takes an array of data and a work partition
public Summer(int[] data, WorkPartitioner.Part part) {
// Call the constructor of the parent class (Worker) with data and part
super(data, part);
}
// Getter method to retrieve the sum
public long getSum() {
return sum;
}
}
package summers;
import worker.WorkPartitioner;
import java.util.concurrent.Callable;
public class SummerCallable extends Summer implements Callable<Long> {
// Constructor taking data and a WorkPartitioner.Part to extract start and finish indices
public SummerCallable(int[] data, WorkPartitioner.Part part) {
super(data, part);
}
// Override the call method from Callable interface
@Override
public Long call() throws Exception {
// Initialize the sum for this callable instance
this.sum = 0;
// Loop through the data within the specified partition and count prime numbers
for (int i = start; i < finish; i++) {
if (isPrime(i)) {
sum++;
}
}
// Return the sum calculated by this callable instance
return sum;
}
// Private method to check if a number is prime, used in the call method
private boolean isPrime(int num) {
if (num <= 1) {
return false;
}
if (num <= 3) {
return true;
}
if (num % 2 == 0 || num % 3 == 0) {
return false;
}
for (int i = 5; i * i <= num; i += 6) {
if (num % i == 0 || num % (i + 2) == 0) {
return false;
}
}
return true;
}
}
\ No newline at end of file
package summers;
import worker.WorkPartitioner;
public class SummerRunnable extends Summer implements Runnable{
// Private variable to store the thread associated with this runnable
private Thread thread;
// Constructor taking data and a WorkPartitioner.Part to extract start and finish indices
public SummerRunnable(int[] data, WorkPartitioner.Part part) {
super(data, part);
}
// Override the run method from the Runnable interface
@Override
public void run() {
sum = 0;
// Loop through the data within the specified partition and count prime numbers
for (int i = start; i < finish; i++) {
if (isPrime(i)) {
sum++;
}
}
}
// Method to start the associated thread
public void startThread() {
thread = new Thread(this);
thread.start();
}
// Method to wait for the associated thread to finish
public void joinThread() throws InterruptedException {
thread.join();
}
// Private method to check if a number is prime, used in the run method
private boolean isPrime(int num) {
if (num <= 1) {
return false;
}
if (num <= 3) {
return true;
}
if (num % 2 == 0 || num % 3 == 0) {
return false;
}
for (int i = 5; i * i <= num; i += 6) {
if (num % i == 0 || num % (i + 2) == 0) {
return false;
}
}
return true;
}
}
\ No newline at end of file
package summers;
import worker.WorkPartitioner;
public class SummerThread extends Thread{
// Private variables to store the start and finish indices, the data array, and the sum
private int start;
private int finish;
private int[] data;
private long sum;
// Constructor taking data, start, and finish indices
public SummerThread(int[] data, int start, int finish) {
this.start = start;
this.finish = finish;
this.data = data;
}
// Constructor taking data and a WorkPartitioner.Part to extract start and finish indices
public SummerThread(int[] data, WorkPartitioner.Part part) {
this(data, part.getStart(), part.getFinish());
}
// Override the run method from the Thread class
@Override
public void run() {
// Initialize the sum for this thread instance
sum = 0;
// Loop through the data within the specified partition and count prime numbers
for (int i = start; i < finish; i++) {
if (isPrime(i)) {
sum++;
}
}
}
// Private method to check if a number is prime, used in the run method
private boolean isPrime(int num) {
if (num <= 1) {
return false;
}
if (num <= 3) {
return true;
}
if (num % 2 == 0 || num % 3 == 0) {
return false;
}
for (int i = 5; i * i <= num; i += 6) {
if (num % i == 0 || num % (i + 2) == 0) {
return false;
}
}
return true;
}
// Getter method to retrieve the sum
public long getSum() {
return sum;
}
}
\ No newline at end of file
package worker;
import java.util.ArrayList;
import java.util.List;
public class WorkPartitioner {
public static List<Part> partitions(int size, int workerCount){
List<Part> parts = new ArrayList<>();
int part = (int) Math.ceil( (double)size / workerCount);
for (int i = 0; i <workerCount ; i++) {
int start = i * part;
int finish = Math.min((i+1) * part, size);
parts.add(new Part(start, finish));
}
return parts;
}
public static class Part {
private final int start;
private final int finish;
public Part(int start, int finish) {
this.start = start;
this.finish = finish;
}
public int getStart() {
return start;
}
public int getFinish() {
return finish;
}
}
}
package worker;
// Abstract class representing a generic worker for parallel processing
public abstract class Worker {
// Protected variables to store the start and finish indices and the data array
protected final int start;
protected final int finish;
protected final int[] data;
// Constructor taking data, start, and finish indices
public Worker(int[] data, int start, int finish) {
this.start = start;
this.finish = finish;
this.data = data;
}
// Constructor taking data and a WorkPartitioner.Part to extract start and finish indices
public Worker(int[] data, WorkPartitioner.Part part) {
this(data, part.getStart(), part.getFinish());
}
// Getter method to retrieve the start index
public int getStart() {
return start;
}
// Getter method to retrieve the finish index
public int getFinish() {
return finish;
}
// Getter method to retrieve the data array
public int[] getData() {
return data;
}
}
import static org.junit.jupiter.api.Assertions.*;
import data.TestData;
import org.example.SequentialSummer;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import parallelsummers.*;
class ParallelSummersTest {
private static int size;
private static int[] data;
private static long result;
@BeforeAll
public static void initialize() {
size = 10000;
data = TestData.createData(size);
result = SequentialSummer.sum(data);
}
private static void testAll(int threadCount) {
assertEquals(result, ParallelSummer0.sum(data,threadCount));
assertEquals(result, ParallelSummer1.sum(data,threadCount));
assertEquals(result, ParallelSummer2.sum(data,threadCount));
assertEquals(result, ParallelSummer3.sum(data,threadCount));
assertEquals(result, ParallelSummer4.sum(data,threadCount));
}
@Test
void testSingleThread() {
testAll(1);
}
@Test
void testTwoThread() {
testAll(2);
}
@Test
void testFourThread() {
testAll(4);
}
@Test
void testEightThread() {
testAll(8);
}
@Test
void testSixteenThread() {
testAll(16);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment