Using 10 Threads to Process an Array - java

I'm working to improve my java skills but a little unsure on how to handle this multi-threaded application. Basically, the program reads a text file and finds the largest number. I added a for loop within my search algorithm to create 10 threads but I'm not sure if it's actually creating 10 threads. The idea is to improve the execution time, or at least that's what I assume should happen. Is there anyway to check if I did it correctly and if the execution time is indeed improved?
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ProcessDataFile {
public static void main(String[] args) throws IOException {
int max = Integer.MIN_VALUE;
int i = 0;
int[] numbers = new int[100000];
String datafile = "dataset529.txt"; //string which contains datafile
String line; //current line of text file
try (BufferedReader br = new BufferedReader(new FileReader(datafile))) { //reads in the datafile
while ((line = br.readLine()) != null) { //reads through each line
numbers[i++] = Integer.parseInt(line); //pulls out the number of each line and puts it in numbers[]
}
}
for (i = 0; i < 10000; i++){ //loop to go through each number in the file and compare it to find the largest int.
for(int j = 0; j < 10; j++) { //creates 10 threads
new Thread();
}
if (max < numbers[i]) //As max gets bigger it checks the array and keeps increasing it as it finds a larger int.
max = numbers[i]; //Sets max equal to the final highest value found.
}
System.out.println("The largest number in DataSet529 is: " + max);
}
}

This is a VERY basic example which demonstrates the basic concepts of creating and running threads which process a given range of values from a specific array. The example makes a few assumptions (only a even number of elements for example). The example is also slightly long winded and is done so deliberately, in an attempt to demonstrate the basic steps which would be needed
Start by taking a look at the Concurrency Trail for more details
import java.util.Random;
public class ThreadExample {
public static void main(String[] args) {
int[] numbers = new int[100000];
Random rnd = new Random();
for (int index = 0; index < numbers.length; index++) {
numbers[index] = rnd.nextInt();
}
Thread[] threads = new Thread[10];
Worker[] workers = new Worker[10];
int range = numbers.length / 10;
for (int index = 0; index < 10; index++) {
int startAt = index * range;
int endAt = startAt + range;
workers[index] = new Worker(startAt, endAt, numbers);
}
for (int index = 0; index < 10; index++) {
threads[index] = new Thread(workers[index]);
threads[index].start();
}
boolean isProcessing = false;
do {
isProcessing = false;
for (Thread t : threads) {
if (t.isAlive()) {
isProcessing = true;
break;
}
}
} while (isProcessing);
for (Worker worker : workers) {
System.out.println("Max = " + worker.getMax());
}
}
public static class Worker implements Runnable {
private int startAt;
private int endAt;
private int numbers[];
private int max = Integer.MIN_VALUE;
public Worker(int startAt, int endAt, int[] numbers) {
this.startAt = startAt;
this.endAt = endAt;
this.numbers = numbers;
}
#Override
public void run() {
for (int index = startAt; index < endAt; index++) {
max = Math.max(numbers[index], max);
}
}
public int getMax() {
return max;
}
}
}
A slightly simpler solution would involve the ExecutorService API, which would allow you to offer a series of Callables to the service which would then return a List of Future's. The benefit here is, the service won't return till all the Callables have completed (or have failed), so you don't need constantly check the states of the threads
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadExample {
public static void main(String[] args) {
int[] numbers = new int[100000];
Random rnd = new Random();
for (int index = 0; index < numbers.length; index++) {
numbers[index] = rnd.nextInt();
}
ExecutorService executor = Executors.newFixedThreadPool(10);
Worker[] workers = new Worker[10];
int range = numbers.length / 10;
for (int index = 0; index < 10; index++) {
int startAt = index * range;
int endAt = startAt + range;
workers[index] = new Worker(startAt, endAt, numbers);
}
try {
List<Future<Integer>> results = executor.invokeAll(Arrays.asList(workers));
for (Future<Integer> future : results) {
System.out.println(future.get());
}
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
public static class Worker implements Callable<Integer> {
private int startAt;
private int endAt;
private int numbers[];
public Worker(int startAt, int endAt, int[] numbers) {
this.startAt = startAt;
this.endAt = endAt;
this.numbers = numbers;
}
#Override
public Integer call() throws Exception {
int max = Integer.MIN_VALUE;
for (int index = startAt; index < endAt; index++) {
max = Math.max(numbers[index], max);
}
return max;
}
}
}

I know this is a bit late answer but you can also use lambda expressions while using ExecutorService instead of creating new class that implements Runnable.
Here is a complete example below, you can play around THREAD_SIZE and RANDOM_ARRAY_SIZE variables.
import org.apache.log4j.Logger;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.*;
public class ConcurrentMaximumTest {
static final int THREAD_SIZE = 10;
static final int RANDOM_ARRAY_SIZE = 8999;
static final SecureRandom RAND = new SecureRandom();
private static Logger logger = Logger.getLogger(ConcurrentMaximumTest.class);
public static void main(String[] args) throws InterruptedException, ExecutionException {
int[] array = generateRandomIntArray(RANDOM_ARRAY_SIZE);
Map<Integer, Integer> positionMap = calculatePositions(array.length, THREAD_SIZE);
ExecutorService threads = Executors.newFixedThreadPool(THREAD_SIZE);
List<Callable<Integer>> toRun = new ArrayList<>(THREAD_SIZE);
for (Map.Entry<Integer, Integer> entry : positionMap.entrySet())
toRun.add(() -> findMax(array, entry.getKey(), entry.getValue()));
int result = Integer.MIN_VALUE;
List<Future<Integer>> futures = threads.invokeAll(toRun);
for (Future<Integer> future : futures) {
Integer localMax = future.get();
if(localMax > result)
result = localMax;
}
threads.shutdownNow();
logger.info("Max value calculated with " + THREAD_SIZE + " threads:" + result);
Arrays.sort(array);
int resultCrosscheck = array[array.length - 1];
logger.info("Max value calculated with sorting: " + resultCrosscheck);
assert result != resultCrosscheck : "Crosscheck failed";
}
/* Calculates start and end positions of each chunk(for simplicity). It can also be calculated on the fly.*/
private static Map<Integer, Integer> calculatePositions(int size, int numThreads){
int lengthOfChunk = size / numThreads;
int remainder = size % numThreads;
int start = 0;
Map<Integer,Integer> result = new LinkedHashMap<>();
for(int i = 0; i < numThreads -1; i++){
result.put(start, lengthOfChunk);
start += lengthOfChunk;
}
result.put(start, lengthOfChunk+remainder);
return result;
}
/*Find maximum value of given part of an array, from start position and chunk size.*/
private static int findMax(int[] wholeArray, int position, int size){
int end = (position + size);
int max = Integer.MIN_VALUE;
logger.info("Starting read for interval [" + position + "," + end + ")");
for(int i = position; i < (position + size); i++)
if(wholeArray[i] > max)
max = wholeArray[i];
logger.info("Finishing finding maximum for interval [" + position + "," + end + ")" + ". Calculated local maximum is " + max);
return max;
}
/* Helper function for generating random int array */
private static int[] generateRandomIntArray(int size){
int[] result = new int[size];
for (int i = 0; i < size; i++)
result[i] = RAND.nextInt(Integer.MAX_VALUE);
return result;
}
}

Related

How to determine your programs runtime?

I have to write a divide-and-conquer program to solve the following problem. Let A[1..n] and B[1..n] be two arrays of distinct integers, each sorted in an increasing order.Find the nth smallest of the 2n combined elements. I can not merge the two arrays. My program must be in O(log n) time.
I have written my program but have no clue how to determine if it meets the requirement of the run time.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.Scanner;
public class main {
public static void main(String[] args) {
// this section of code will require user input to have the value of n to be set
Scanner sc = new Scanner(System.in);
System.out.print(("What number would you like to set n equal to : "));
int value = sc.nextInt();
// this section of code set the two array only to hold the value of n
Random rand = new Random();
ArrayList<Integer> setA = new ArrayList<Integer>();
for (int i = 0; i < value; i++) {
int picks = rand.nextInt(1000);
setA.add(picks);
}
Collections.sort(setA);
System.out.println("A1: "+ setA);
ArrayList<Integer> setX = new ArrayList<Integer>();
for (int k = 0; k < value; k++) {
int picks = rand.nextInt(1000);
setX.add(picks);
}
Collections.sort(setX);
System.out.println("A2: "+ setX);
ArrayList<Integer> afinal = new ArrayList<Integer>();
int r = 0;
int f = 0;
int q = 0;
while(afinal.size()!= value) {
if(setA.get(r) < setX.get(f)) {
q = setA.get(r);
afinal.add(q);
r++;
}else {
q = setX.get(f);
afinal.add(q);
f++;
}
}
System.out.println("");
System.out.println(afinal);
int w = value - 1;
int ans = afinal.get(w);
System.out.println("");
System.out.println("The nth smallest integer is "+ ans);
}
}
You can calculate runtime with System.currentTimeMillis().
What we want to do is get the current time before the program runs, then get the time after the program runs. We then handle the two times accordingly.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.Scanner;
public class main {
public static void main(String[] args) {
long before = System.currentTimeMillis();
// this section of code will require user input to have the value of n to be set
Scanner sc = new Scanner(System.in);
System.out.print(("What number would you like to set n equal to : "));
int value = sc.nextInt();
// this section of code set the two array only to hold the value of n
Random rand = new Random();
ArrayList<Integer> setA = new ArrayList<Integer>();
for (int i = 0; i < value; i++) {
int picks = rand.nextInt(1000);
setA.add(picks);
}
Collections.sort(setA);
System.out.println("A1: "+ setA);
ArrayList<Integer> setX = new ArrayList<Integer>();
for (int k = 0; k < value; k++) {
int picks = rand.nextInt(1000);
setX.add(picks);
}
Collections.sort(setX);
System.out.println("A2: "+ setX);
ArrayList<Integer> afinal = new ArrayList<Integer>();
int r = 0;
int f = 0;
int q = 0;
while(afinal.size()!= value) {
if(setA.get(r) < setX.get(f)) {
q = setA.get(r);
afinal.add(q);
r++;
}else {
q = setX.get(f);
afinal.add(q);
f++;
}
}
System.out.println("");
System.out.println(afinal);
int w = value - 1;
int ans = afinal.get(w);
System.out.println("");
System.out.println("The nth smallest integer is "+ ans);
long after = System.currentTimeMillis();
}
}
In the example above, we've got the two times. Now what do we do?
We can subtract them to get the difference in times (by milliseconds).
Like so:
long ellapsedMilliseconds = after-before;
If you want to get the elapsed time in seconds, divided ellapsedMilliseconds by 100.
So the final code should be;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.Scanner;
public class main {
public static void main(String[] args) {
long before = System.currentTimeMillis();
// this section of code will require user input to have the value of n to be set
Scanner sc = new Scanner(System.in);
System.out.print(("What number would you like to set n equal to : "));
int value = sc.nextInt();
// this section of code set the two array only to hold the value of n
Random rand = new Random();
ArrayList<Integer> setA = new ArrayList<Integer>();
for (int i = 0; i < value; i++) {
int picks = rand.nextInt(1000);
setA.add(picks);
}
Collections.sort(setA);
System.out.println("A1: "+ setA);
ArrayList<Integer> setX = new ArrayList<Integer>();
for (int k = 0; k < value; k++) {
int picks = rand.nextInt(1000);
setX.add(picks);
}
Collections.sort(setX);
System.out.println("A2: "+ setX);
ArrayList<Integer> afinal = new ArrayList<Integer>();
int r = 0;
int f = 0;
int q = 0;
while(afinal.size()!= value) {
if(setA.get(r) < setX.get(f)) {
q = setA.get(r);
afinal.add(q);
r++;
}else {
q = setX.get(f);
afinal.add(q);
f++;
}
}
System.out.println("");
System.out.println(afinal);
int w = value - 1;
int ans = afinal.get(w);
System.out.println("");
System.out.println("The nth smallest integer is "+ ans);
long after = System.currentTimeMillis();
long ellapsedMilliseconds = after-before;
System.out.println("Execution time: "+elapsedMilliseconds);
}
}

How do I raise the value of counter if there is random number generated?

So basically, I have an array with 50 random numbers and I want to raise the number of count if I get the same random number. After all that counting I want to see how often I got which number but I seem to have a error in my counting method. So it basically gives me, for example, more zeros than there are in random generated numbers.
This is my program code:
import java.util.Random;
public class aufgabe2 {
static int[] ran = new int[50];
static int[] counter = new int[10];
static Random r = new Random();
public static void ranNums() {
for (int i = 0; i < ran.length; i++) {
ran[i] = r.nextInt(10);
System.out.print(ran[i] + " ");
}
}
public static void countSameRanNums(int[] counter) {
System.out.println();
for (int i = 0; i < 9; i++) {
counter[ran[i]]++;
System.out.println(i + ".frequency: " + (counter[ran[i]]));
}
}
public static void main(String[] args) {
ranNums();
countSameRanNums(ran);
}
}
The problem that you have it is that you are only iterating the first 9 positions of ran array, that has 50 positions.
Furthermore, I think you do not need two methods to get the count of each random number. You can count them at the same time you get the random values. Look at this:
public static void ranNums() {
for (int i = 0; i < ran.length; i++) {
ran[i] = r.nextInt(10);
System.out.print(ran[i] + " ");
counter[ran[i]]++;
}
}
And after you only have to make a loop to get all the counts:
for(int j = 0; j < counter.length; j++)
{
System.out.println(j + ".frequency: " + (counter[j]));
}
import java.util.Random;
public class aufgabe2 {
static int[] ran = new int[50];
static int[] counter = new int[10];
static Random r = new Random();
public static void ranNums() {
int counter;
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for (int i = 0; i < ran.length; i++) {
counter = 1;
ran[i] = r.nextInt(10);
if(map.containsKey(ran[i])){
map.put(ran[i], map.get(ran[i])+1);
} else {
map.put(ran[i], counter);
}
}
Iterator<?> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getKey() + " frequency : " + pair.getValue());
}
}
public static void main(String[] args) {
ranNums();
}
}
Let me know if you need anything above to be explained

How can I rewrite this code to use object oriented programming?

public class stackOverflow
{
public static void main (String args[])
{
int maxNumbers = 100;
int numbers[] = new int[maxNumbers];
for (int k = 0; k < numbers.length; k++)
numbers[k] = getRandom(10,99);
for (int k = 0; k < numbers.length; k++)
System.out.print(numbers[k] + " ");
System.out.println();
}
public static int getRandom(int min, int max)
{
int range = max - min + 1;
double rndDouble = Math.random() * range;
int rndInt = (int) rndDouble + min;
return rndInt;
}
}
The provided program works correctly, but I didn't write it very neatly/professionally. Can anyone give me some guidance on how I could rewrite this to implement Object Oriented Programming under a List class?
This can be an alternative...
class Numbers {
int maxNumbers;
int numbers[];
public Numbers(int maxNumbers) {
// TODO Auto-generated constructor stub
this.maxNumbers = maxNumbers;
this.numbers = new int[maxNumbers];
}
public int getRandom(int min, int max) {
int range = max - min + 1;
double rndDouble = Math.random() * range;
int rndInt = (int) rndDouble + min;
return rndInt;
}
}
public class StackOverflow {
public static void main(String [] args) {
Numbers numbers = new Numbers(100);
for (int k = 0; k < numbers.numbers.length; k++)
numbers.numbers[k] = numbers.getRandom(10,99);
for (int k = 0; k < numbers.numbers.length; k++)
System.out.print(numbers.numbers[k] + " ");
}
}
Or something like this...
public class StackOverflow {
static int maxNumbers = 100;
static int numbers[] = new int[maxNumbers];
public static void main (String args[]) {
StackOverflow stackOverflow = new StackOverflow();
for (int k = 0; k < numbers.length; k++)
numbers[k] = stackOverflow.getRandom(10,99);
for (int k = 0; k < numbers.length; k++)
System.out.print(numbers[k] + " ");
System.out.println();
}
public int getRandom(int min, int max) {
int range = max - min + 1;
double rndDouble = Math.random() * range;
int rndInt = (int) rndDouble + min;
return rndInt;
}
}
Friend, There are a numbers of alternatives.
import java.util.ArrayList;
import java.util.List;
int maxNumbers = 100;
List<Integer> numbers = new ArrayList<Integer>();
for (int k = 0; k < maxNumbers; k++)
numbers.add( getRandom(10,99) );
System.out.println(numbers.toString());
is this kind of what you wanted?
A Number of ways to write this structured linear program in oops. Here is my version..
public class stackOverflow
{
int numbers[];
public stackOverflow(){ //assuming default constructor will provide a 100 length array
int maxNumbers = 100;
this.numbers[] = new int[maxNumbers];
}
public stackOverflow(int length){ //Provide a length according to your need.
this.numbers[] = new int[length];
}
private void fillNumberArrayWithRandomNumber(){
for (int k = 0; k < this.numbers.length; k++)
numbers[k] = this.getRandom(10,99);
}
private void printAllNumbersInArray(){
for (int k = 0; k < this.numbers.length; k++)
System.out.print(numbers[k] + " ");
System.out.println();
}
public static void main (String args[])
{
stackOverflow obj1 = new stackOverflow(); //default Constructor will call with array lenth 100
obj1.fillNumberArrayWithRandomNumber();
obj1.printAllNumbersInArray();
stackOverflow obj2 = new stackOverflow(50); //overloaded Constructor will Call with array length 50
obj2.fillNumberArrayWithRandomNumber();
obj2.printAllNumbersInArray();
}
public int getRandom(int min, int max)
{
int range = max - min + 1;
double rndDouble = Math.random() * range;
int rndInt = (int) rndDouble + min;
return rndInt;
}
}
Another Way to separate the business logic to the other class. and calling it From Others.
public class GenerateRandomNumbers
{
int numbers[];
public GenerateRandomNumbers(){ //assuming default constructor will provide a 100 length array
int maxNumbers = 100;
this.numbers[] = new int[maxNumbers];
}
public GenerateRandomNumbers(int length){ //Provide a length according to your need.
this.numbers[] = new int[length];
}
public void fillNumberArrayWithRandomNumber(){
for (int k = 0; k < this.numbers.length; k++)
numbers[k] = this.getRandom(10,99);
}
public void printAllNumbersInArray(){
for (int k = 0; k < this.numbers.length; k++)
System.out.print(numbers[k] + " ");
System.out.println();
}
private int getRandom(int min, int max)
{
int range = max - min + 1;
double rndDouble = Math.random() * range;
int rndInt = (int) rndDouble + min;
return rndInt;
}
}
class stackOverflow {
public static void main (String args[])
{
GenerateRandomNumbers obj1 = new GenerateRandomNumbers(); //default Constructor will call with array lenth 100
obj1.fillNumberArrayWithRandomNumber();
obj1.printAllNumbersInArray();
GenerateRandomNumbers obj2 = new GenerateRandomNumbers(50); //overloaded Constructor will Call with array length 50
obj2.fillNumberArrayWithRandomNumber();
obj2.printAllNumbersInArray();
}
}
You could do it with Random (and nextInt(int)), and in Java 8+ a lambda expression. That might look something like
int maxNumbers = 100;
int min = 10;
int max = 99;
Random rand = new Random();
List<Integer> al = new ArrayList<>();
IntStream.range(0, maxNumbers).forEach(x -> {
al.add(rand.nextInt(max - min) + min);
});
al.stream().forEach(x -> {
System.out.printf("%d%n", x);
});
Using a list:
The following is an example class that utilizes an object list to hold each number. You can either declare a max size with the parameterized constructor or you could use the default constructor which sets it to 100.
The setNumbers method will never execute more than once (by checking the size to the max size) so that the list never gets larger than the max size. Also, you could add parameters to the setNumbers method so that you could choose the range that each random number would be between rather than just 10-99. The getNumbers method will return the list object which contains all of the numbers.
import java.util.ArrayList;
import java.util.List;
public class Example {
int maxNumbers;
List<Object> list = new ArrayList<Object>();
public Example(){
this.maxNumbers = 100;
}
public Example( int max){
this.maxNumbers = max;
}
private int getRandom(int min, int max)
{
int range = max - min + 1;
double rndDouble = Math.random() * range;
int rndInt = (int) rndDouble + min;
return rndInt;
}
public List<Object> getNumbers(){
return this.list;
}
public void setNumbers(){
if (list.size() >= maxNumbers){
return;
}
for (int k = 0; k < this.maxNumbers; k++)
this.list.add(getRandom(10,99));
}
}
Here is an example for a driver class.
public class ExampleDriver
{
public static void main (String args[])
{
//Instantiates the object using the default constructor.
Example test = new Example();
//Generates the numbers within the list.
test.setNumbers();
//Stores the returned list from getNumbers() to exampleList
List<Object> exampleList = test.getNumbers();
}
}
You can create your own RandomList which extends ArrayList<Integer>:
public class RandomList extends ArrayList<Integer> {
private int size;
private int min;
private int max;
public RandomList(int size, int min, int max) {
super(size);
this.size = size;
this.min = min;
this.max = max;
}
/**
* Populate list with entries between min and max
*/
public void populate() {
Random rand = new Random();
for (int t = 0; t < size; t++) {
this.add(rand.nextInt(max - min) + min);
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (Integer i:this) {
sb.append(i).append(" ");
}
return sb.toString();
}
public static void main (String [] args) {
RandomList randomList = new RandomList(100, 10, 99);
randomList.populate();
System.out.println(randomList);
}
}
You could implement your own List class. For that you should define a Node, a List class (which will contain nodes) and a Service that will be responsible to create the random numbers.
This service will be represented in a singleton (a class that cannot be instantiated by any other class).
public class MyRandom {
private static MyRandom rdn = new MyRandom();
private MyRandom() {}
public static MyRandom getInstance() {
return rdn;
}
public int getRandom(int min, int max) {
int range = max - min + 1;
double rndDouble = Math.random() * range;
int rndInt = (int) rndDouble + min;
return rndInt;
}
}
The Node will only contain a value (the random number) and a reference to the next node. This is the Node class
public class MyNode {
private final int value;
private MyNode next;
public MyNode(int value) {
this.value = value;
next = null;
}
public void setNext(MyNode next) {
this.next = next;
}
public int getValue() {
return value;
}
public MyNode getNext() {
return next;
}
}
The List class will contain a reference to the root node, which will also be responsible to add new nodes to the list.
Keep in mind that you could use Generics as well.
public final class MyList {
private MyNode root;
public MyList(int maxNumber) {
for (int i = 0; i < maxNumber; i++) {
addNode(MyRandom.getInstance().getRandom(0, 99));
}
}
public boolean isEmpty() {
return root == null;
}
public void addNode(int value) {
if (isEmpty()) {
root = new MyNode(value);
} else {
MyNode aux = root;
while (aux.getNext() != null)
aux = aux.getNext();
aux.setNext(new MyNode(value));
}
}
public void printList() {
if (!isEmpty()) {
MyNode aux = root;
while (aux.getNext() != null) {
System.out.println(aux.getValue());
aux = aux.getNext();
}
System.out.println(aux.getValue());
}
}
}
And the Main must only instantiate the MyList class and call the printList to show the list.
public class Main {
public static void main(String[] args) {
MyList lista = new MyList(10);
lista.printList();
}
}
Hope this helps you.

Java - (Print distinct numbers)

I'm trying to solve this problem:
"Write a program that reads in ten numbers and displays the number of distinct numbers and the distinct numbers separated by exactly one space."
My code at the moment does not save all distinct numbers and at time repeatedly display 0. If anyone can see where my logic has gone wrong, any tip will be helpful. Thank you!
public class PracticeProject
{
public static void main(String args[])
{
int[] number = new int[10];
int[] counter = new int[10];
int numcounter = 0;
numGen(number);
numcounter = distNum(number, counter, numcounter);
dispDist(counter, numcounter);
}
public static void numGen(int[] number)
{
Random rand = new Random();
for (int i = 0; i < number.length; i++)
{
number[i] = rand.nextInt(10);
System.out.print(number[i] + " ");
}
System.out.println();
}
public static int distNum(int[] number, int[] counter, int numcounter)
{
for (int i = 0; i < number.length; i++)
{
for (int j = 0; j <= i; j++)
{
if (counter[j] == number[i])
{
break;
}
if (j == i)
{
counter[j] = number[i];
numcounter++;
}
}
}
return numcounter;
}
public static void dispDist(int[] counter, int numcounter)
{
for (int i = 0; i < numcounter; i++)
{
System.out.print(counter[i] + " ");
}
}
}
The problem is with the logic in your distNum() method, which was not correctly removing all duplicates from the output array. Try using this version instead:
public static int distNum(int[] number, int[] counter, int numcounter) {
for (int i = 0; i < number.length; i++) {
boolean isUnique = true;
for (int j = 0; j < numcounter; j++) {
if (counter[j] == number[i]) {
isUnique = false;
break;
}
}
if (isUnique) {
counter[numcounter] = number[i];
numcounter++;
}
}
return numcounter;
}
I walk through the array of random numbers, and for each one I scan counter to see if the value has already been encountered. If it be a duplicate, then it does not get added to the unique list.
This method was tested along with the rest of your original code using IntelliJ, and it appears to be working correctly.
If you use array to store your counters, you need to set default value, otherwise if your array have multiple 0, it distinguish by equaling. because the int array default values is 0.
and you can use list or vector to store your counters.
public static void main(String args[]) {
int[] number = new int[10];
int[] counter = new int[10];
List<Integer> counters = new ArrayList<Integer>();
int numcounter = 0;
numGen(number);
numcounter = distNum(number, counters, numcounter);
dispDist(counters, numcounter);
}
public static void numGen(int[] number) {
Random rand = new Random();
for (int i = 0; i < number.length; i++) {
number[i] = rand.nextInt(10);
System.out.print(number[i] + " ");
}
System.out.println();
}
public static int distNum(int[] number, List<Integer> counters, int numcounter) {
for (int i : number) {
if (!counters.contains(i)){
counters.add(i);
}
}
return numcounter;
}
public static void dispDist(List<Integer> counter, int numcounter) {
for (Integer i : counter) {
System.out.print(i + " ");
}
}
Try put below function .... using Hashmap... Key will be no. and value will be the disctinct time it occured.
public void duplicate(int[] a) {
HashMap<Integer, Integer> h = new HashMap<Integer, Integer>();
for (int i = 0; i < a.length; i++) {
Integer j = (int) h.put(a[i], 1);
if (j != null) { // checking if already in hashmap
h.put(a[i], j + 1); // if there then incrementing value
}
}
Iterator it = h.entrySet().iterator(); // displaying value you can have you logic here
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
System.out.println(pair.getKey() + " = " + pair.getValue());
it.remove(); // avoids a ConcurrentModificationException
}
}

Parallel implementation of Levenshtein distance slows down with more threads

This is a parallel implementation of Levenshtein distance that I was writing for fun. I'm disappointed in the results. I am running this on a core i7 processor, so I have plenty of available threads. However, as I increase the thread count, the performance degrades significantly. By that I mean it actually runs slower with more threads for input of the same size.
I was hoping that someone could look at the way I am using threads, and the java.util.concurrent package, and tell me if I am doing anything wrong. I'm really only interested in reasons why the parallelism is not working as I would expect. I don't expect the reader to look at the complicated indexing going on here. I believe the calculations I'm doing are correct. But even if they are not, I think I should still be seeing a close to linear speed-up as I increase the number of threads in the threadpool.
I've included the benchmarking code I used. I'm using libraries found here for benchmarking. The second code block is what I used for benchmarking.
Thanks for any help :).
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class EditDistance {
private static final int MIN_CHUNK_SIZE = 5;
private final ExecutorService threadPool;
private final int threadCount;
private final String maxStr;
private final String minStr;
private final int maxLen;
private final int minLen;
public EditDistance(String s1, String s2, ExecutorService threadPool,
int threadCount) {
this.threadCount = threadCount;
this.threadPool = threadPool;
if (s1.length() < s2.length()) {
minStr = s1;
maxStr = s2;
} else {
minStr = s2;
maxStr = s1;
}
maxLen = maxStr.length();
minLen = minStr.length();
}
public int editDist() {
int iterations = maxLen + minLen - 1;
int[] prev = new int[0];
int[] current = null;
for (int i = 0; i < iterations; i++) {
int currentLen;
if (i < minLen) {
currentLen = i + 1;
} else if (i < maxLen) {
currentLen = minLen;
} else {
currentLen = iterations - i;
}
current = new int[currentLen * 2 - 1];
parallelize(prev, current, currentLen, i);
prev = current;
}
return current[0];
}
private void parallelize(int[] prev, int[] current, int currentLen,
int iteration) {
int chunkSize = Math.max(current.length / threadCount, MIN_CHUNK_SIZE);
List<Future<?>> futures = new ArrayList<Future<?>>(currentLen);
for (int i = 0; i < currentLen; i += chunkSize) {
int stopIdx = Math.min(currentLen, i + chunkSize);
Runnable worker = new Worker(prev, current, currentLen, iteration,
i, stopIdx);
futures.add(threadPool.submit(worker));
}
for (Future<?> future : futures) {
try {
Object result = future.get();
if (result != null) {
throw new RuntimeException(result.toString());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// We can only finish the computation if we complete
// all subproblems
throw new RuntimeException(e);
}
}
}
private void doChunk(int[] prev, int[] current, int currentLen,
int iteration, int startIdx, int stopIdx) {
int mergeStartIdx = (iteration < minLen) ? 0 : 2;
for (int i = startIdx; i < stopIdx; i++) {
// Edit distance
int x;
int y;
int leftIdx;
int downIdx;
int diagonalIdx;
if (iteration < minLen) {
x = i;
y = currentLen - i - 1;
leftIdx = i * 2 - 2;
downIdx = i * 2;
diagonalIdx = i * 2 - 1;
} else {
x = i + iteration - minLen + 1;
y = minLen - i - 1;
leftIdx = i * 2;
downIdx = i * 2 + 2;
diagonalIdx = i * 2 + 1;
}
int left = 1 + ((leftIdx < 0) ? iteration + 1 : prev[leftIdx]);
int down = 1 + ((downIdx < prev.length) ? prev[downIdx]
: iteration + 1);
int diagonal = penalty(x, y)
+ ((diagonalIdx < 0 || diagonalIdx >= prev.length) ? iteration
: prev[diagonalIdx]);
int dist = Math.min(left, Math.min(down, diagonal));
current[i * 2] = dist;
// Merge prev
int mergeIdx = i * 2 + 1;
if (mergeIdx < current.length) {
current[mergeIdx] = prev[mergeStartIdx + i * 2];
}
}
}
private int penalty(int maxIdx, int minIdx) {
return (maxStr.charAt(maxIdx) == minStr.charAt(minIdx)) ? 0 : 1;
}
private class Worker implements Runnable {
private final int[] prev;
private final int[] current;
private final int currentLen;
private final int iteration;
private final int startIdx;
private final int stopIdx;
Worker(int[] prev, int[] current, int currentLen, int iteration,
int startIdx, int stopIdx) {
this.prev = prev;
this.current = current;
this.currentLen = currentLen;
this.iteration = iteration;
this.startIdx = startIdx;
this.stopIdx = stopIdx;
}
#Override
public void run() {
doChunk(prev, current, currentLen, iteration, startIdx, stopIdx);
}
}
public static void main(String args[]) {
int threadCount = 4;
ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
EditDistance ed = new EditDistance("Saturday", "Sunday", threadPool,
threadCount);
System.out.println(ed.editDist());
threadPool.shutdown();
}
}
There is a private inner class Worker inside EditDistance. Each worker is responsible for filling in a range of the current array using EditDistance.doChunk. EditDistance.parallelize is responsible for creating those workers, and waiting for them to finish their tasks.
And the code I am using for benchmarks:
import java.io.PrintStream;
import java.util.concurrent.*;
import org.apache.commons.lang3.RandomStringUtils;
import bb.util.Benchmark;
public class EditDistanceBenchmark {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: <string length> <thread count>");
System.exit(1);
}
PrintStream oldOut = System.out;
System.setOut(System.err);
int strLen = Integer.parseInt(args[0]);
int threadCount = Integer.parseInt(args[1]);
String s1 = RandomStringUtils.randomAlphabetic(strLen);
String s2 = RandomStringUtils.randomAlphabetic(strLen);
ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
Benchmark b = new Benchmark(new Benchmarker(s1, s2, threadPool,threadCount));
System.setOut(oldOut);
System.out.println("threadCount: " + threadCount +
" string length: "+ strLen + "\n\n" + b);
System.out.println("s1: " + s1 + "\ns2: " + s2);
threadPool.shutdown();
}
private static class Benchmarker implements Runnable {
private final String s1, s2;
private final int threadCount;
private final ExecutorService threadPool;
private Benchmarker(String s1, String s2, ExecutorService threadPool, int threadCount) {
this.s1 = s1;
this.s2 = s2;
this.threadPool = threadPool;
this.threadCount = threadCount;
}
#Override
public void run() {
EditDistance d = new EditDistance(s1, s2, threadPool, threadCount);
d.editDist();
}
}
}
It's very easy to accidentally write code that does not parallelize very well. A main culprit is when your threads compete for underlying system resources (e.g. a cache line). Since this algorithm inherently acts on things that are close to each other in physical memory, I suspect pretty strongly that may be the culprit.
I suggest you review this excellent article on False Sharing
http://www.drdobbs.com/go-parallel/article/217500206?pgno=3
and then carefully review your code for cases where threads would block one another.
Additionally, running more threads than you have CPU cores will slow down performance if your threads are CPU bound (if you're already using all cores to near 100%, adding more threads will only add overhead for the context switches).

Categories