Archived
0

don't use frok join on build graph

fix multi thread search
This commit is contained in:
iMoHax
2015-07-30 11:58:47 +03:00
parent 011158edf1
commit d1ac962d3d
9 changed files with 263 additions and 169 deletions

View File

@@ -6,6 +6,9 @@ import ru.trader.analysis.graph.Traversal;
public interface RouteSpecification<T> { public interface RouteSpecification<T> {
public boolean specified(Edge<T> edge, Traversal<T> entry); public boolean specified(Edge<T> edge, Traversal<T> entry);
public default boolean updateSpecified(Edge<T> edge, Traversal<T> entry){
return specified(edge, entry);
}
default RouteSpecification<T> and(final RouteSpecification<T> other){ default RouteSpecification<T> and(final RouteSpecification<T> other){
return (edge, entry) -> RouteSpecification.this.specified(edge, entry) && other.specified(edge, entry); return (edge, entry) -> RouteSpecification.this.specified(edge, entry) && other.specified(edge, entry);

View File

@@ -7,7 +7,6 @@ import ru.trader.analysis.graph.*;
import ru.trader.core.*; import ru.trader.core.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ForkJoinTask;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -62,7 +61,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
private void runDeferredTasks(){ private void runDeferredTasks(){
deferredTasks.sort((b1,b2) -> Integer.compare(b2.getDeep(), b1.getDeep())); deferredTasks.sort((b1,b2) -> Integer.compare(b2.getDeep(), b1.getDeep()));
for (VendorsGraphBuilder task : deferredTasks) { for (VendorsGraphBuilder task : deferredTasks) {
ForkJoinTask.invokeAll(task); task.compute();
} }
deferredTasks.clear(); deferredTasks.clear();
} }
@@ -104,54 +103,45 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
} }
@Override @Override
protected void build() { protected void compute() {
if (isAdding){ if (isAdding){
if (!vertex.locker().tryLock()){ addAlreadyCheckedEdges();
throw new ConcurrentModificationException("Adding must do in single thread");
} else {
try {
addAlreadyCheckedEdges();
} finally {
vertex.locker().unlock();
}
}
} else { } else {
super.build(); super.compute();
} }
} }
@Override @Override
protected double checkConnect(Vendor buyer) { protected BuildHelper<Vendor> createHelper(Vendor buyer) {
double nextlimit = super.checkConnect(buyer); BuildHelper<Vendor> helper = super.createHelper(buyer);
Vendor seller = vertex.getEntry(); if (helper.isConnected()){
if (nextlimit > 0){ Vendor seller = vertex.getEntry();
if (buyer instanceof TransitVendor && (deep == 0 || seller.getPlace().equals(buyer.getPlace()))){ if (buyer instanceof TransitVendor && (deep == 0 || seller.getPlace().equals(buyer.getPlace()))){
LOG.trace("Buyer is transit of seller or is end, skipping"); LOG.trace("Buyer is transit of seller or is end, skipping");
nextlimit = -1; return new BuildHelper<>(buyer, -1);
} }
if (seller instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())){ if (seller instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())){
LOG.trace("Seller is transit of buyer, skipping"); LOG.trace("Seller is transit of buyer, skipping");
nextlimit = -1; return new BuildHelper<>(buyer, -1);
} }
} }
return nextlimit; return helper;
} }
@Override @Override
protected void connect(Vertex<Vendor> next, double nextLimit) { protected void connect(Edge<Vendor> edge) {
BuildEdge e; if (edge instanceof VendorsBuildEdge){
super.connect(edge);
}
}
@Override
protected BuildEdge createEdge(BuildHelper<Vendor> helper, Vertex<Vendor> next) {
BuildEdge cEdge = super.createEdge(helper, next);
if (next.getEntry() instanceof TransitVendor){ if (next.getEntry() instanceof TransitVendor){
e = super.createEdge(next); return cEdge;
} else {
e = createEdge(next);
vertex.connect(e);
} }
addSubTask(e, nextLimit);
}
@Override
protected VendorsBuildEdge createEdge(Vertex<Vendor> target) {
BuildEdge cEdge = super.createEdge(target);
if (vertex.getEntry() instanceof TransitVendor){ if (vertex.getEntry() instanceof TransitVendor){
addEdgesToHead(cEdge); addEdgesToHead(cEdge);
} }
@@ -193,8 +183,8 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
private void addAlreadyCheckedEdges(){ private void addAlreadyCheckedEdges(){
LOG.trace("Adding already checked vertex"); LOG.trace("Adding already checked vertex");
vertex.getEdges().parallelStream().forEach(aEdge -> { vertex.getEdges().parallelStream().forEach(edge -> {
VendorsBuildEdge e = (VendorsBuildEdge) aEdge; VendorsBuildEdge e = (VendorsBuildEdge) edge;
if (callback.isCancel()) return; if (callback.isCancel()) return;
Vendor entry = e.getTarget().getEntry(); Vendor entry = e.getTarget().getEntry();
LOG.trace("Check {}", entry); LOG.trace("Check {}", entry);
@@ -247,25 +237,22 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
} }
} }
private void addSubTask(BuildEdge e, double nextLimit){ @Override
Vertex<Vendor> next = e.getTarget(); protected GraphBuilder createSubTask(Edge<Vendor> edge, Collection<Vendor> set, int deep, double limit) {
// If level > deep when vertex already added on upper deep return new VendorsGraphBuilder(this, (BuildEdge) edge, set, deep, limit);
if (next.getLevel() < deep || next.getEntry() instanceof TransitVendor) { }
boolean adding = next.getLevel() >= deep;
if (deep > 0 || adding) { @Override
//Recursive build protected void addSubTask(Edge<Vendor> edge, double nextLimit) {
VendorsGraphBuilder task = new VendorsGraphBuilder(this, e, set, deep - 1, nextLimit); Vertex<Vendor> next = edge.getTarget();
task.isAdding = adding; if (next.getLevel() >= deep && next.getEntry() instanceof TransitVendor) {
if (adding){ if (deep > 0){
holdTask(task); VendorsGraphBuilder task = new VendorsGraphBuilder(this, (BuildEdge) edge, set, deep - 1, nextLimit);
} else { task.isAdding = true;
task.fork(); holdTask(task);
subTasks.add(task);
}
} }
} else {
LOG.trace("Vertex {} already check", next);
} }
super.addSubTask(edge, nextLimit);
} }
} }
@@ -533,7 +520,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
protected boolean check(Edge<Vendor> e){ protected boolean check(Edge<Vendor> e){
VendorsBuildEdge edge = (VendorsBuildEdge) e; VendorsBuildEdge edge = (VendorsBuildEdge) e;
return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill()) return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill())
&& (edge.getProfit() > 0 || isFound(edge, this)); && (edge.getProfit() > 0 || VendorsCrawler.this.isFound(edge, this));
} }
protected VendorsEdge wrap(Edge<Vendor> e) { protected VendorsEdge wrap(Edge<Vendor> e) {

View File

@@ -5,21 +5,17 @@ import org.slf4j.LoggerFactory;
import ru.trader.analysis.AnalysisCallBack; import ru.trader.analysis.AnalysisCallBack;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ForkJoinPool; import java.util.stream.Collectors;
import java.util.concurrent.RecursiveAction;
public abstract class AbstractGraph<T> implements Graph<T> { public abstract class AbstractGraph<T> implements Graph<T> {
private final static ForkJoinPool POOL = new ForkJoinPool();
//TODO: make it worked in multi thread
private final static int THRESHOLD = 1;
private final static Logger LOG = LoggerFactory.getLogger(AbstractGraph.class); private final static Logger LOG = LoggerFactory.getLogger(AbstractGraph.class);
protected Vertex<T> root; protected Vertex<T> root;
protected final List<Vertex<T>> vertexes; protected final List<Vertex<T>> vertexes;
protected final GraphCallBack callback; protected final GraphCallBack callback;
protected int minJumps; protected int minJumps;
private final ReentrantLock lock = new ReentrantLock();
protected AbstractGraph() { protected AbstractGraph() {
this(new AnalysisCallBack()); this(new AnalysisCallBack());
@@ -27,16 +23,17 @@ public abstract class AbstractGraph<T> implements Graph<T> {
protected AbstractGraph(AnalysisCallBack callback) { protected AbstractGraph(AnalysisCallBack callback) {
this.callback = new GraphCallBack(callback); this.callback = new GraphCallBack(callback);
vertexes = new CopyOnWriteArrayList<>(); vertexes = new ArrayList<>();
} }
protected abstract RecursiveAction createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit); protected abstract GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
public void build(T start, Collection<T> set, int maxDeep, double limit) { public void build(T start, Collection<T> set, int maxDeep, double limit) {
callback.startBuild(start); callback.startBuild(start);
minJumps = 1; minJumps = 1;
root = getInstance(start, maxDeep, maxDeep); root = getInstance(start, maxDeep, maxDeep);
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit)); GraphBuilder builder = createGraphBuilder(root, set, maxDeep - 1, limit);
builder.compute();
onEnd(); onEnd();
callback.endBuild(); callback.endBuild();
} }
@@ -49,21 +46,22 @@ public abstract class AbstractGraph<T> implements Graph<T> {
return new Vertex<>(entry, index); return new Vertex<>(entry, index);
} }
protected Vertex<T> getInstance(T entry, int level, int deep){ private Vertex<T> getInstance(T entry, int level, int deep){
Vertex<T> vertex = getVertex(entry).orElse(null); Vertex<T> vertex = null;
if (vertex == null) { lock.lock();
synchronized (vertexes){ try {
vertex = getVertex(entry).orElse(null); vertex = getVertex(entry).orElse(null);
if (vertex == null){ if (vertex == null){
LOG.trace("Is new vertex"); LOG.trace("Is new vertex");
vertex = newInstance(entry, vertexes.size()); vertex = newInstance(entry, vertexes.size());
vertex.setLevel(level); vertexes.add(vertex);
vertexes.add(vertex); vertex.setLevel(level);
int jumps = root != null ? root.getLevel() - deep : 0; int jumps = root != null ? root.getLevel() - deep : 0;
if (jumps > minJumps) if (jumps > minJumps)
minJumps = jumps; minJumps = jumps;
}
} }
} finally {
lock.unlock();
} }
return vertex; return vertex;
} }
@@ -103,8 +101,7 @@ public abstract class AbstractGraph<T> implements Graph<T> {
return vertexes.size(); return vertexes.size();
} }
protected abstract class GraphBuilder extends RecursiveAction { protected abstract class GraphBuilder {
protected final List<RecursiveAction> subTasks = new ArrayList<>(THRESHOLD);
protected final Vertex<T> vertex; protected final Vertex<T> vertex;
protected final Collection<T> set; protected final Collection<T> set;
protected final int deep; protected final int deep;
@@ -117,70 +114,89 @@ public abstract class AbstractGraph<T> implements Graph<T> {
this.limit = limit; this.limit = limit;
} }
protected abstract double checkConnect(T entry); protected BuildHelper<T> createHelper(final T entry){
protected abstract Edge<T> createEdge(Vertex<T> target); return new BuildHelper<>(entry, limit);
protected RecursiveAction createSubTask(Vertex<T> vertex, Collection<T> set, int deep, double limit){ }
return createGraphBuilder(vertex, set, deep, limit); protected abstract Edge<T> createEdge(BuildHelper<T> helper, Vertex<T> target);
protected void connect(Edge<T> edge){
vertex.connect(edge);
}
protected GraphBuilder createSubTask(Edge<T> edge, Collection<T> set, int deep, double limit){
return createGraphBuilder(edge.getTarget(), set, deep, limit);
} }
@Override protected void compute() {
protected final void compute() { List<BuildHelper<T>> helpers;
vertex.locker().lock(); if (vertex.getLevel() <= deep){
try { vertex.setLevel(deep+1);
if (vertex.getLevel() <= deep){ } else {
vertex.setLevel(deep+1); if (vertex.getLevel() > deep+1){
LOG.trace("Already build");
return;
} }
} finally {
vertex.locker().unlock();
} }
build(); helpers = build();
runSubTasks(helpers);
} }
protected void build(){ private List<BuildHelper<T>> build(){
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep); LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
for (T entry : set) { List<BuildHelper<T>> helpers = set.parallelStream()
if (callback.isCancel()) break; .filter(entry -> entry != vertex.getEntry())
if (entry == vertex.getEntry()) continue; .map(this::createHelper)
double nextLimit = checkConnect(entry); .filter(BuildHelper::isConnected)
if (nextLimit >= 0) { .collect(Collectors.toList());
LOG.trace("Connect {} to {}", vertex, entry); helpers.parallelStream().forEach(this::connect);
Vertex<T> next = getInstance(entry, 0, deep);
connect(next, nextLimit);
} else {
LOG.trace("Vertex {} is far away", entry);
}
if (subTasks.size() >= THRESHOLD) {
joinSubTasks();
}
}
if (!subTasks.isEmpty()){
joinSubTasks();
}
LOG.trace("End build graph from {} on deep {}", vertex, deep); LOG.trace("End build graph from {} on deep {}", vertex, deep);
return helpers;
} }
protected void connect(Vertex<T> next, double nextLimit){ private void connect(final BuildHelper<T> helper){
vertex.connect(createEdge(next)); LOG.trace("Connect {} to {}", vertex, helper.entry);
Vertex<T> next = getInstance(helper.entry, 0, deep);
Edge<T> edge = createEdge(helper, next);
helper.setEdge(edge);
connect(edge);
}
private void runSubTasks(final List<BuildHelper<T>> helpers){
LOG.trace("Build sub graph from {}, limit {}, deep {}", vertex, limit, deep);
for (BuildHelper<T> helper : helpers) {
if (callback.isCancel()) break;
addSubTask(helper.edge, helper.nextLimit);
}
LOG.trace("End build sub graph from {}, limit {}, deep {}", vertex, limit, deep);
}
protected void addSubTask(Edge<T> edge, double nextLimit){
Vertex<T> next = edge.getTarget();
if (next.getLevel() < deep) { if (next.getLevel() < deep) {
if (deep > 0) { if (deep > 0) {
//Recursive build //Recursive build
RecursiveAction task = createSubTask(next, set, deep - 1, nextLimit); GraphBuilder task = createSubTask(edge, set, deep - 1, nextLimit);
task.fork(); task.compute();
subTasks.add(task);
} }
} }
} }
}
protected void joinSubTasks(){ protected class BuildHelper<T> {
for (RecursiveAction subTask : subTasks) { private final T entry;
if (callback.isCancel()){ private final double nextLimit;
subTask.cancel(true); private Edge<T> edge;
} else {
subTask.join(); public BuildHelper(T entry, double nextLimit) {
} this.entry = entry;
} this.nextLimit = nextLimit;
subTasks.clear(); this.edge = null;
} }
private void setEdge(Edge<T> edge) {
this.edge = edge;
}
public boolean isConnected(){
return nextLimit >= 0;
}
} }
} }

View File

@@ -42,33 +42,32 @@ public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T>
} }
protected class ConnectibleGraphBuilder extends GraphBuilder { protected class ConnectibleGraphBuilder extends GraphBuilder {
protected double minFuel;
protected double maxFuel;
protected double distance;
protected ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) { protected ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
super(vertex, set, deep, limit); super(vertex, set, deep, limit);
} }
@Override @Override
protected double checkConnect(T entry) { protected BuildHelper<T> createHelper(T entry) {
distance = vertex.getEntry().getDistance(entry); double distance = vertex.getEntry().getDistance(entry);
if (distance > getShip().getMaxJumpRange()){ if (distance > getShip().getMaxJumpRange()){
LOG.trace("Vertex {} is far away, {}", entry, distance); LOG.trace("Vertex {} is far away, {}", entry, distance);
return -1; return new BuildHelper<>(entry,-1);
} }
maxFuel = getShip().getMaxFuel(distance); double maxFuel = getShip().getMaxFuel(distance);
minFuel = getShip().getMinFuel(distance); double minFuel = getShip().getMinFuel(distance);
double fuel = getProfile().withRefill() ? vertex.getEntry().canRefill() ? getShip().getRoundMaxFuel(distance) : limit : getShip().getTank(); double fuel = getProfile().withRefill() ? vertex.getEntry().canRefill() ? getShip().getRoundMaxFuel(distance) : limit : getShip().getTank();
double fuelCost = getShip().getFuelCost(fuel, distance); double fuelCost = getShip().getFuelCost(fuel, distance);
return fuel - fuelCost; double nextLimit = getProfile().withRefill() ? fuel - fuelCost : fuel;
return new CBuildHelper<>(entry, nextLimit, minFuel, maxFuel, distance);
} }
@Override @Override
protected BuildEdge createEdge(Vertex<T> target) { protected BuildEdge createEdge(BuildHelper<T> helper, Vertex<T> target) {
CBuildHelper h = (CBuildHelper) helper;
BuildEdge res = new BuildEdge(vertex, target); BuildEdge res = new BuildEdge(vertex, target);
res.setFuel(minFuel, maxFuel); res.setFuel(h.minFuel, h.maxFuel);
res.setDistance(distance); res.setDistance(h.distance);
return res; return res;
} }
} }
@@ -132,4 +131,19 @@ public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T>
} }
} }
public class CBuildHelper<T> extends BuildHelper<T> {
private final double minFuel;
private final double maxFuel;
private final double distance;
private CBuildHelper(T entry, double nextLimit, double minFuel, double maxFuel, double distance) {
super(entry, nextLimit);
this.minFuel = minFuel;
this.maxFuel = maxFuel;
this.distance = distance;
}
}
} }

View File

@@ -60,7 +60,11 @@ public class Crawler<T> {
} }
protected boolean isFound(Edge<T> edge, Traversal<T> head){ protected boolean isFound(Edge<T> edge, Traversal<T> head){
return specification.specified(edge, head); return isFound(edge, head, false);
}
private boolean isFound(Edge<T> edge, Traversal<T> head, boolean updateStates){
return updateStates ? specification.updateSpecified(edge, head) : specification.specified(edge, head);
} }
public int getMaxSize() { public int getMaxSize() {
@@ -100,7 +104,7 @@ public class Crawler<T> {
if (maxDeep < 0) maxDeep = 0; if (maxDeep < 0) maxDeep = 0;
found = bfs(start(s), maxDeep, count); found = bfs(start(s), maxDeep, count);
} else { } else {
found = dfs(start(s), Math.min(t.get().getLevel() + 1, s.getLevel()), count); found = dfs(start(s), t.get().getLevel() + 1, count);
} }
} }
LOG.debug("Found {} paths", found); LOG.debug("Found {} paths", found);
@@ -158,7 +162,7 @@ public class Crawler<T> {
boolean stop = false; boolean stop = false;
if (deep == source.getLevel()){ if (deep == source.getLevel()){
for (Edge<T> next : entry.getEdges()) { for (Edge<T> next : entry.getEdges()) {
if (isFound(next, entry)){ if (isFound(next, entry, true)){
List<Edge<T>> res = getCopyList(entry, next); List<Edge<T>> res = getCopyList(entry, next);
LOG.debug("Last edge found, path {}", res); LOG.debug("Last edge found, path {}", res);
found++; found++;
@@ -170,9 +174,13 @@ public class Crawler<T> {
} }
} }
if (!stop && found < count){ if (!stop && found < count){
if (deep <= source.getLevel() && entry.size() < maxSize-1) { if (deep < source.getLevel() && entry.size() < maxSize-1) {
LOG.trace("Search around"); LOG.trace("Search around");
for (Edge<T> edge : entry.getEdges()) { for (Edge<T> edge : entry.getEdges()) {
if (entry.isSkipped()){
LOG.trace("Is skipped");
break;
}
if (edge.getTarget().isSingle()) continue; if (edge.getTarget().isSingle()) continue;
found += dfs(travers(entry, edge), deep, count-found); found += dfs(travers(entry, edge), deep, count-found);
if (found >= count) break; if (found >= count) break;
@@ -190,6 +198,10 @@ public class Crawler<T> {
queue.add(root); queue.add(root);
while (!queue.isEmpty() && count > found){ while (!queue.isEmpty() && count > found){
CostTraversalEntry entry = queue.poll(); CostTraversalEntry entry = queue.poll();
if (entry.isSkipped()){
LOG.trace("Is skipped");
continue;
}
Vertex<T> source = entry.vertex; Vertex<T> source = entry.vertex;
if (entry.size() >= maxSize){ if (entry.size() >= maxSize){
LOG.trace("Is limit deep"); LOG.trace("Is limit deep");
@@ -199,7 +211,7 @@ public class Crawler<T> {
Iterator<Edge<T>> iterator = entry.iterator(); Iterator<Edge<T>> iterator = entry.iterator();
while (iterator.hasNext()){ while (iterator.hasNext()){
Edge<T> edge = iterator.next(); Edge<T> edge = iterator.next();
if (isFound(edge, entry)){ if (isFound(edge, entry, true)){
List<Edge<T>> res = getCopyList(entry, edge); List<Edge<T>> res = getCopyList(entry, edge);
LOG.debug("Last edge found, path {}", res); LOG.debug("Last edge found, path {}", res);
found++; found++;
@@ -207,6 +219,10 @@ public class Crawler<T> {
break; break;
} }
} }
if (entry.isSkipped()){
LOG.trace("Is skipped");
break;
}
if (found >= count) break; if (found >= count) break;
if (edge.getTarget().isSingle()) continue; if (edge.getTarget().isSingle()) continue;
if (deep < source.getLevel()) { if (deep < source.getLevel()) {
@@ -226,10 +242,14 @@ public class Crawler<T> {
queue.add(root); queue.add(root);
while (!queue.isEmpty() && count > found){ while (!queue.isEmpty() && count > found){
CostTraversalEntry entry = queue.poll(); CostTraversalEntry entry = queue.poll();
if (entry.isSkipped()){
LOG.trace("Is skipped");
continue;
}
LOG.trace("Check path entry {}, weight {}", entry, entry.weight); LOG.trace("Check path entry {}, weight {}", entry, entry.weight);
Edge<T> edge = entry.getEdge(); Edge<T> edge = entry.getEdge();
if (edge != null) { if (edge != null) {
if (isFound(edge, entry)) { if (isFound(edge, entry, true)) {
List<Edge<T>> res = entry.toEdges(); List<Edge<T>> res = entry.toEdges();
LOG.debug("Path found {}", res); LOG.debug("Path found {}", res);
found++; found++;
@@ -238,6 +258,10 @@ public class Crawler<T> {
} }
if (found >= count) break; if (found >= count) break;
} }
if (entry.isSkipped()){
LOG.trace("Is skipped");
continue;
}
if (edge.getTarget().isSingle()){ if (edge.getTarget().isSingle()){
continue; continue;
} }
@@ -288,6 +312,10 @@ public class Crawler<T> {
if (found >= count) break; if (found >= count) break;
CTEntrySupport next = targetsQueue.peek(); CTEntrySupport next = targetsQueue.peek();
limit = next != null ? next.entry.getWeight() : Double.NaN; limit = next != null ? next.entry.getWeight() : Double.NaN;
if (deep > entry.getTarget().getLevel() || entry.size() >= maxSize){
LOG.trace("Is limit deep");
continue;
}
} }
if (alreadyFound + found < count){ if (alreadyFound + found < count){
LOG.trace("Continue search, limit {}", limit); LOG.trace("Continue search, limit {}", limit);
@@ -295,10 +323,6 @@ public class Crawler<T> {
LOG.trace("Already {} found, extracting", alreadyFound); LOG.trace("Already {} found, extracting", alreadyFound);
continue; continue;
} }
if (deep >= entry.getTarget().getLevel() || entry.size() >= maxSize){
LOG.trace("Is limit deep");
continue;
}
DFS task = new DFS(curr, deep, count - found, limit); DFS task = new DFS(curr, deep, count - found, limit);
POOL.invoke(task); POOL.invoke(task);
targetsQueue.addAll(task.getTargets()); targetsQueue.addAll(task.getTargets());
@@ -371,7 +395,8 @@ public class Crawler<T> {
} }
private class DFS extends RecursiveAction { private class DFS extends RecursiveAction {
private final CTEntrySupport root; private CTEntrySupport root;
private CTEntrySupport curr;
private final int count; private final int count;
private final int deep; private final int deep;
private final Collection<CTEntrySupport> queue; private final Collection<CTEntrySupport> queue;
@@ -411,14 +436,52 @@ public class Crawler<T> {
return queue; return queue;
} }
private boolean cancel(){
if (isCancelled()) return true;
if (root.entry.isSkipped()){
LOG.trace("Root skipped");
if (isSubTask){
LOG.trace("Stop sub task");
return true;
} else {
curr = root;
}
}
return false;
}
private boolean skip(){
if (curr.entry.isSkipped()){
while (curr.entry.isSkipped()){
LOG.trace("Is skipped, return to prev level");
if (!levelUp()) break;
}
return true;
}
return false;
}
private boolean levelUp(){
if (isRoot(curr)) return false;
assert curr.parent != null;
LOG.trace("Return to prev level");
if (curr == root){
root = root.parent;
}
curr = curr.parent;
return true;
}
private void search(){ private void search(){
CTEntrySupport curr = root; curr = root;
LOG.trace("Start {}", root); LOG.trace("Start {}", root);
while (curr.hasNext()){ while (curr.hasNext()){
if (cancel()) break;
Edge<T> edge = curr.next(); Edge<T> edge = curr.next();
CostTraversalEntry entry = curr.entry; CostTraversalEntry entry = curr.entry;
if (skip()) continue;
LOG.trace("Check edge {}, entry {}, weight {}", edge, entry, entry.weight); LOG.trace("Check edge {}, entry {}, weight {}", edge, entry, entry.weight);
boolean isTarget = isFound(edge, entry); boolean isTarget = isFound(edge, entry, true);
boolean canDeep = !entry.getTarget().isSingle() && deep < entry.getTarget().getLevel() && entry.size() < maxSize-1; boolean canDeep = !entry.getTarget().isSingle() && deep < entry.getTarget().getLevel() && entry.size() < maxSize-1;
if (canDeep || isTarget){ if (canDeep || isTarget){
CostTraversalEntry nextEntry = travers(entry, edge); CostTraversalEntry nextEntry = travers(entry, edge);
@@ -430,8 +493,9 @@ public class Crawler<T> {
LOG.trace("Found, add entry {} to queue", nextEntry); LOG.trace("Found, add entry {} to queue", nextEntry);
targets.add(curr); targets.add(curr);
limit = Double.isNaN(limit) ? nextEntry.getWeight() : Math.min(limit, nextEntry.getWeight()); limit = Double.isNaN(limit) ? nextEntry.getWeight() : Math.min(limit, nextEntry.getWeight());
curr = curr.parent; levelUp();
} else { } else {
if (skip()) continue;
if (!Double.isNaN(limit) && nextEntry.getWeight() >= limit){ if (!Double.isNaN(limit) && nextEntry.getWeight() >= limit){
if (targets.size() < count){ if (targets.size() < count){
LOG.trace("Not found, limit {}, add entry {} to queue", limit, nextEntry); LOG.trace("Not found, limit {}, add entry {} to queue", limit, nextEntry);
@@ -439,24 +503,22 @@ public class Crawler<T> {
} else { } else {
LOG.trace("Not found, limit {}, don't add entry {} to queue", limit, nextEntry); LOG.trace("Not found, limit {}, don't add entry {} to queue", limit, nextEntry);
} }
if (!isRoot(curr.parent)){ levelUp();
curr = curr.parent.parent; if (!levelUp()){
} else {
break; break;
} }
} else { } else {
if (!isRoot(curr) && maxSize-nextEntry.size() < SPLIT_SIZE){ if (!isRoot(curr) && maxSize-nextEntry.size() < SPLIT_SIZE){
if (addSubTask(curr)) if (addSubTask(curr))
curr = curr.parent; levelUp();
} }
} }
} }
} else { } else {
LOG.trace("Is limit deep"); LOG.trace("Is limit deep");
} }
while (!curr.hasNext() && !isRoot(curr)){ while (!curr.hasNext() && levelUp()){
LOG.trace("Level complete, return to prev level"); LOG.trace("Level complete");
curr = curr.parent;
} }
} }
LOG.trace("Done {}", root); LOG.trace("Done {}", root);
@@ -503,6 +565,7 @@ public class Crawler<T> {
private final Edge<T> edge; private final Edge<T> edge;
private List<Edge<T>> edges; private List<Edge<T>> edges;
private Integer size; private Integer size;
private transient boolean skipped;
protected TraversalEntry(Vertex<T> vertex) { protected TraversalEntry(Vertex<T> vertex) {
this.vertex = vertex; this.vertex = vertex;
@@ -515,6 +578,7 @@ public class Crawler<T> {
this.head = head; this.head = head;
this.vertex = edge.getTarget(); this.vertex = edge.getTarget();
this.edge = edge; this.edge = edge;
this.skipped = head.isSkipped();
edges = null; edges = null;
} }
@@ -550,6 +614,16 @@ public class Crawler<T> {
getEdges().sort(Comparator.<Edge<T>>naturalOrder()); getEdges().sort(Comparator.<Edge<T>>naturalOrder());
} }
@Override
public void setSkipped(boolean skipped) {
this.skipped = skipped;
}
@Override
public boolean isSkipped() {
return skipped;
}
protected List<Edge<T>> collect(Collection<Edge<T>> src){ protected List<Edge<T>> collect(Collection<Edge<T>> src){
return new ArrayList<>(src); return new ArrayList<>(src);
} }

View File

@@ -9,6 +9,8 @@ public interface Traversal<T> {
List<Edge<T>> getEdges(); List<Edge<T>> getEdges();
Iterator<Edge<T>> iterator(); Iterator<Edge<T>> iterator();
void sort(); void sort();
void setSkipped(boolean skipped);
boolean isSkipped();
default boolean isConnect(T target){ default boolean isConnect(T target){
Edge<T> edge = getEdge(); Edge<T> edge = getEdge();

View File

@@ -10,7 +10,7 @@ public class Vertex<T> {
private final T entry; private final T entry;
private final int index; private final int index;
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
private volatile int level = -1; private int level = -1;
public Vertex(T entry, int index) { public Vertex(T entry, int index) {
this.entry = entry; this.entry = entry;
@@ -37,16 +37,15 @@ public class Vertex<T> {
return level; return level;
} }
public ReentrantLock locker(){
return lock;
}
public void connect(Edge<T> edge){ public void connect(Edge<T> edge){
assert this == edge.getSource(); assert this == edge.getSource();
synchronized (edges){ lock.lock();
try {
if (!edges.contains(edge)){ if (!edges.contains(edge)){
edges.add(edge); edges.add(edge);
} }
} finally {
lock.unlock();
} }
} }

View File

@@ -248,13 +248,14 @@ public class Ship {
private double ladenJumpRange = Double.NaN; private double ladenJumpRange = Double.NaN;
private void fillFuelTable(){ private void fillFuelTable(){
double fuel = getEngine().getMaxFuel(); double fuel = getEngine().getMaxFuel();
fuelTable = new FuelHelper[(int) (fuel/FUEL_TABLE_STEP)]; FuelHelper[] fuelTable = new FuelHelper[(int) (fuel/FUEL_TABLE_STEP)];
maxJumpRange = Double.NaN; ladenJumpRange = Double.NaN; maxJumpRange = Double.NaN; ladenJumpRange = Double.NaN;
for (int i = fuelTable.length - 1; i >= 0; i--) { for (int i = fuelTable.length - 1; i >= 0; i--) {
double distance = getJumpRange(fuel); double distance = getJumpRange(fuel);
fuelTable[i] = new FuelHelper(distance, fuel); fuelTable[i] = new FuelHelper(distance, fuel);
fuel = fuel - FUEL_TABLE_STEP; fuel = fuel - FUEL_TABLE_STEP;
} }
this.fuelTable = fuelTable;
} }
public double getMaxFuel(double distance){ public double getMaxFuel(double distance){

View File

@@ -118,7 +118,7 @@ public class CrawlerTest extends Assert {
paths.clear(); paths.clear();
crawler.findMin(x4, 20); crawler.findMin(x4, 20);
TestUtil.assertPaths(true, paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4), TestUtil.assertPaths(paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4),
PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x5, x4), PPath.of(x5, x4, x3, x4), PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x5, x4), PPath.of(x5, x4, x3, x4),
PPath.of(x5, x4, x6, x4), PPath.of(x5, x6, x3, x4), PPath.of(x5, x4, x6, x4), PPath.of(x5, x6, x3, x4),
PPath.of(x5, x3, x5, x4), PPath.of(x5, x4, x2, x4), PPath.of(x5, x3, x5, x4), PPath.of(x5, x4, x2, x4),
@@ -240,7 +240,8 @@ public class CrawlerTest extends Assert {
paths.clear(); paths.clear();
crawler.findFast(x2); crawler.findFast(x2);
TestUtil.assertPaths(paths.get(), PPath.of(x5, x3, x2)); assertNotNull(paths.get());
assertEquals(2, paths.get().get(0).size());
paths.clear(); paths.clear();
} }
@@ -277,9 +278,6 @@ public class CrawlerTest extends Assert {
TestUtil.assertPaths(paths.get(), PPath.of(x6, x4, x2)); TestUtil.assertPaths(paths.get(), PPath.of(x6, x4, x2));
paths.clear(); paths.clear();
crawler.findFast(x6, x2);
TestUtil.assertPaths(paths.get(), PPath.of(x6, x4, x2));
paths.clear();
} }
@After @After