diff --git a/DIRECTORY.md b/DIRECTORY.md index 0c9aeca4e59a..4afb2a60a623 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -563,6 +563,7 @@ * [SelfAdjustingScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SelfAdjustingScheduling.java) * [SJFScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SJFScheduling.java) * [SlackTimeScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SlackTimeScheduling.java) + * [SpeculativeExecutionScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SpeculativeExecutionScheduling.java) * [SRTFScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SRTFScheduling.java) * searches * [BinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/BinarySearch.java) @@ -597,6 +598,7 @@ * [UnionFind](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/UnionFind.java) * [UpperBound](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/UpperBound.java) * slidingwindow + * [LongestSubarrayWithSumLessOrEqualToK](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToK.java) * [LongestSubstringWithoutRepeatingCharacters](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java) * [MaxSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java) * [MinSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java) @@ -1193,6 +1195,7 @@ * [SelfAdjustingSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SelfAdjustingSchedulingTest.java) * [SJFSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SJFSchedulingTest.java) * [SlackTimeSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SlackTimeSchedulingTest.java) + * [SpeculativeExecutionSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SpeculativeExecutionSchedulingTest.java) * [SRTFSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SRTFSchedulingTest.java) * searches * [BinarySearch2dArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java) @@ -1228,6 +1231,7 @@ * [UnionFindTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/UnionFindTest.java) * [UpperBoundTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/UpperBoundTest.java) * slidingwindow + * [LongestSubarrayWithSumLessOrEqualToKTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToKTest.java) * [LongestSubstringWithoutRepeatingCharactersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharactersTest.java) * [MaxSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java) * [MinSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java) diff --git a/src/main/java/com/thealgorithms/scheduling/SpeculativeExecutionScheduling.java b/src/main/java/com/thealgorithms/scheduling/SpeculativeExecutionScheduling.java new file mode 100644 index 000000000000..3959a847a494 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/SpeculativeExecutionScheduling.java @@ -0,0 +1,82 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * SpeculativeExecutionScheduling runs multiple copies of the same task in parallel, + * picking the first one that finishes successfully. It is used to mitigate the + * impact of stragglers (slow tasks). + * + * Use Case: Big data systems like Hadoop and Spark, where it helps improve job completion time. + * + * @author Hardvan + */ +public final class SpeculativeExecutionScheduling { + + static class Task { + String name; + boolean completed; + long startTime; + + Task(String name) { + this.name = name; + this.completed = false; + this.startTime = -1; + } + + void start() { + this.startTime = System.currentTimeMillis(); + } + + void complete() { + this.completed = true; + } + + boolean hasStarted() { + return this.startTime != -1; + } + } + + private final Map> taskGroups; + + public SpeculativeExecutionScheduling() { + taskGroups = new HashMap<>(); + } + + /** + * Adds a task to the specified group. + * + * @param groupName the name of the group + * @param taskName the name of the task + */ + public void addTask(String groupName, String taskName) { + List tasks = taskGroups.computeIfAbsent(groupName, k -> new ArrayList<>()); + tasks.add(new Task(taskName)); + } + + /** + * Executes the tasks in the specified group by assigning a start time. + * + * @param groupName the name of the group + * @return the name of the task that completed successfully + */ + public String executeTasks(String groupName) { + List tasks = taskGroups.get(groupName); + if (tasks == null) { + return null; + } + for (Task task : tasks) { + if (!task.completed) { + if (!task.hasStarted()) { + task.start(); + } + task.complete(); + return task.name + " started at " + task.startTime; + } + } + return null; + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/SpeculativeExecutionSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/SpeculativeExecutionSchedulingTest.java new file mode 100644 index 000000000000..5a6c95f37c28 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/SpeculativeExecutionSchedulingTest.java @@ -0,0 +1,87 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SpeculativeExecutionSchedulingTest { + + private SpeculativeExecutionScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new SpeculativeExecutionScheduling(); + } + + @Test + public void testAddAndExecuteTask() { + scheduler.addTask("Group1", "Task1"); + + String result = scheduler.executeTasks("Group1"); + + // Check for null before splitting + if (result != null) { + String[] parts = result.split(" started at "); + + // Validate task name and ensure start time is a valid timestamp + assertEquals("Task1", parts[0]); + assertTrue(Long.parseLong(parts[1]) > 0, "Start time should be greater than 0"); + } else { + // Handle the case where result is null + assertTrue(false, "The result should not be null after executing the task."); + } + } + + @Test + public void testMultipleTasksInGroup() { + scheduler.addTask("Group1", "Task1"); + scheduler.addTask("Group1", "Task2"); + + // Execute the first task + String result1 = scheduler.executeTasks("Group1"); + + // Check for null before splitting + if (result1 != null) { + String[] parts1 = result1.split(" started at "); + assertEquals("Task1", parts1[0]); + assertTrue(Long.parseLong(parts1[1]) > 0, "Start time should be greater than 0"); + } else { + // Handle the case where result1 is null + assertTrue(false, "The result for Task1 should not be null."); + } + + // Execute the second task + String result2 = scheduler.executeTasks("Group1"); + + // Check for null before splitting + if (result2 != null) { + String[] parts2 = result2.split(" started at "); + assertEquals("Task2", parts2[0]); + assertTrue(Long.parseLong(parts2[1]) > 0, "Start time should be greater than 0"); + } else { + // Handle the case where result2 is null + assertTrue(false, "The result for Task2 should not be null."); + } + } + + @Test + public void testExecuteAllTasks() { + scheduler.addTask("Group1", "Task1"); + scheduler.addTask("Group1", "Task2"); + + scheduler.executeTasks("Group1"); + scheduler.executeTasks("Group1"); + + // Confirm executing tasks again returns null + assertNull(scheduler.executeTasks("Group1")); + } + + @Test + public void testEmptyTaskGroup() { + // Confirm executing tasks on an empty group returns null + assertNull(scheduler.executeTasks("Group2")); + } +}