/*******************************************************************************
* Copyright 2013 Analog Devices, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************/
package com.analog.lyric.dimple.solvers.core.multithreading.phasealgorithm;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import com.analog.lyric.dimple.exceptions.DimpleException;
import com.analog.lyric.dimple.schedulers.scheduleEntry.IScheduleEntry;
import com.analog.lyric.dimple.solvers.core.multithreading.MultiThreadingManager;
import com.analog.lyric.dimple.solvers.core.multithreading.MultithreadingAlgorithm;
/*
* Divides the dependency graph into multiple phases where each phase contains
* completely independent schedule entries. This method allows the multiple threads
* to maintain their own queues without any interaction while in a single phase.
* There is some interaction in that, if a thread runs out of work, it will steal
* work from another thread.
*/
public class PhaseMultithreadingAlgorithm extends MultithreadingAlgorithm
{
public PhaseMultithreadingAlgorithm(MultiThreadingManager manager)
{
super(manager);
}
/*
* Iteration simply runs through the phases and updates all the schedule
* entries in that phase concurrently.
*/
@Override
public void iterate(int numIters)
{
ArrayList<ArrayList<IScheduleEntry>> phases = getManager().getDependencyGraph().getPhases();
ExecutorService service = getManager().getService();
int numThreads = getManager().getNumWorkers();
for (int i = 0; i < numIters; i++)
{
for (int j = 0; j < phases.size(); j++)
{
updateScheduleEntries(service, phases.get(j), numThreads, true);
}
}
}
/*
* Update all schedule entries assuming there are no dependencies between them.
*/
@SuppressWarnings("unchecked")
public void updateScheduleEntries(ExecutorService service,
ArrayList<IScheduleEntry> scheduleEntries,
int numThreads, boolean stealing)
{
//Provide an array of concurrent linked queues so that each thread can use work
//stealing if they run out of work.
ConcurrentLinkedQueue<IScheduleEntry> [] deques = new ConcurrentLinkedQueue[numThreads];
for (int i = 0; i < deques.length; i++)
deques[i] = new ConcurrentLinkedQueue<IScheduleEntry>();
ArrayList<Callable<Object>> ll = new ArrayList<Callable<Object>>(numThreads);
//Instantiate the Callable object that will do the updates. Each object is responsible
//for filling its queue so that building the queues is also multithreaded.
for (int i = 0; i < numThreads; i++)
ll.add(new WorkerWithStealing(getManager().getSolverGraph(), scheduleEntries, i, deques, stealing));
//Kick off the threads and wait for them to complete.
try {
service.invokeAll(ll);
} catch (InterruptedException e) {
throw new DimpleException(e);
}
}
}