4747import java .util .Collections ;
4848import java .util .Comparator ;
4949import java .util .HashMap ;
50- import java .util .HashSet ;
5150import java .util .Iterator ;
5251import java .util .LinkedList ;
52+ import java .util .LinkedHashSet ;
5353import java .util .List ;
5454import java .util .Map ;
5555import java .util .Random ;
5656import java .util .Set ;
5757import java .util .function .ToDoubleFunction ;
5858import java .util .stream .Collectors ;
59+ import java .util .stream .StreamSupport ;
5960
6061/**
6162 * This graph contains a set of {@link ChannelConversion}s.
@@ -168,7 +169,7 @@ private Tree mergeTrees(Collection<Tree> trees) {
168169 final Tree firstTree = iterator .next ();
169170 Bitmask combinationSettledIndices = new Bitmask (firstTree .settledDestinationIndices );
170171 int maxSettledIndices = combinationSettledIndices .cardinality ();
171- final HashSet <ChannelDescriptor > employedChannelDescriptors = new HashSet <>(firstTree .employedChannelDescriptors );
172+ final LinkedHashSet <ChannelDescriptor > employedChannelDescriptors = new LinkedHashSet <>(firstTree .employedChannelDescriptors );
172173 int maxVisitedChannelDescriptors = employedChannelDescriptors .size ();
173174 double costs = firstTree .costs ;
174175 TreeVertex newRoot = new TreeVertex (firstTree .root .channelDescriptor , firstTree .root .settledIndices );
@@ -222,7 +223,11 @@ public static class CostbasedTreeSelectionStrategy implements TreeSelectionStrat
222223
223224 @ Override
224225 public Tree select (Tree t1 , Tree t2 ) {
225- return t1 .costs <= t2 .costs ? t1 : t2 ;
226+ int cmp = Double .compare (t1 .costs , t2 .costs );
227+ if (cmp == 0 ) {
228+ cmp = t1 .getDeterministicSignature ().compareTo (t2 .getDeterministicSignature ());
229+ }
230+ return cmp <= 0 ? t1 : t2 ;
226231 }
227232
228233 }
@@ -381,7 +386,7 @@ private ShortestTreeSearcher(OutputSlot<?> sourceOutput,
381386 this .existingDestinationChannelIndices = new Bitmask ();
382387
383388 this .collectExistingChannels (sourceChannel );
384- this .openChannelDescriptors = new HashSet <>(openChannels .size ());
389+ this .openChannelDescriptors = new LinkedHashSet <>(openChannels .size ());
385390 for (Channel openChannel : openChannels ) {
386391 this .openChannelDescriptors .add (openChannel .getDescriptor ());
387392 }
@@ -477,7 +482,9 @@ private Set<ChannelDescriptor> resolveSupportedChannels(final InputSlot<?> input
477482 final List <ChannelDescriptor > supportedInputChannels = owner .getSupportedInputChannels (input .getIndex ());
478483 if (input .isLoopInvariant ()) {
479484 // Loop input is needed in several iterations and must therefore be reusable.
480- return supportedInputChannels .stream ().filter (ChannelDescriptor ::isReusable ).collect (Collectors .toSet ());
485+ return supportedInputChannels .stream ()
486+ .filter (ChannelDescriptor ::isReusable )
487+ .collect (Collectors .toCollection (LinkedHashSet ::new ));
481488 } else {
482489 return WayangCollections .asSet (supportedInputChannels );
483490 }
@@ -546,7 +553,7 @@ private void kernelizeChannelRequests() {
546553 }
547554 if (channelDescriptors .size () - numReusableChannels == 1 ) {
548555 iterator .remove ();
549- channelDescriptors = new HashSet <>(channelDescriptors );
556+ channelDescriptors = new LinkedHashSet <>(channelDescriptors );
550557 channelDescriptors .removeIf (channelDescriptor -> !channelDescriptor .isReusable ());
551558 kernelDestChannelDescriptorSetsToIndicesUpdates .add (new Tuple <>(channelDescriptors , indices ));
552559 }
@@ -575,7 +582,7 @@ private void kernelizeChannelRequests() {
575582 */
576583 private Tree searchTree () {
577584 // Prepare the recursive traversal.
578- final HashSet <ChannelDescriptor > visitedChannelDescriptors = new HashSet <>(16 );
585+ final LinkedHashSet <ChannelDescriptor > visitedChannelDescriptors = new LinkedHashSet <>(16 );
579586 visitedChannelDescriptors .add (this .sourceChannelDescriptor );
580587
581588 // Perform the traversal.
@@ -777,7 +784,7 @@ private Set<ChannelDescriptor> getSuccessorChannelDescriptors(ChannelDescriptor
777784 final Channel channel = this .existingChannels .get (descriptor );
778785 if (channel == null || this .openChannelDescriptors .contains (descriptor )) return null ;
779786
780- Set <ChannelDescriptor > result = new HashSet <>();
787+ Set <ChannelDescriptor > result = new LinkedHashSet <>();
781788 for (ExecutionTask consumer : channel .getConsumers ()) {
782789 if (!consumer .getOperator ().isAuxiliary ()) continue ;
783790 for (Channel successorChannel : consumer .getOutputChannels ()) {
@@ -988,7 +995,12 @@ private static class Tree {
988995 *
989996 * @see TreeVertex#channelDescriptor
990997 */
991- private final Set <ChannelDescriptor > employedChannelDescriptors = new HashSet <>();
998+ private final Set <ChannelDescriptor > employedChannelDescriptors = new LinkedHashSet <>();
999+
1000+ /**
1001+ * Cached deterministic signature for tie-breaking.
1002+ */
1003+ private String deterministicSignature ;
9921004
9931005 /**
9941006 * The sum of the costs of all {@link TreeEdge}s of this instance.
@@ -1010,6 +1022,7 @@ static Tree singleton(ChannelDescriptor channelDescriptor, Bitmask settledIndice
10101022 this .root = root ;
10111023 this .settledDestinationIndices = settledDestinationIndices ;
10121024 this .employedChannelDescriptors .add (root .channelDescriptor );
1025+ this .deterministicSignature = null ;
10131026 }
10141027
10151028 /**
@@ -1033,6 +1046,21 @@ void reroot(ChannelDescriptor newRootChannelDescriptor,
10331046 this .employedChannelDescriptors .add (newRootChannelDescriptor );
10341047 this .settledDestinationIndices .orInPlace (newRootSettledIndices );
10351048 this .costs += edge .costEstimate ;
1049+ this .deterministicSignature = null ;
1050+ }
1051+
1052+ private String getDeterministicSignature () {
1053+ if (this .deterministicSignature == null ) {
1054+ final String descriptorSignature = this .employedChannelDescriptors .stream ()
1055+ .map (Object ::toString )
1056+ .sorted ()
1057+ .collect (Collectors .joining ("|" ));
1058+ final String indexSignature = StreamSupport .stream (this .settledDestinationIndices .spliterator (), false )
1059+ .map (String ::valueOf )
1060+ .collect (Collectors .joining ("," ));
1061+ this .deterministicSignature = descriptorSignature + "#" + indexSignature ;
1062+ }
1063+ return this .deterministicSignature ;
10361064 }
10371065
10381066 @ Override
@@ -1090,7 +1118,7 @@ private void copyEdgesFrom(TreeVertex that) {
10901118 * @return a {@link Set} of said {@link ChannelConversion}s
10911119 */
10921120 private Set <ChannelConversion > getChildChannelConversions () {
1093- Set <ChannelConversion > channelConversions = new HashSet <>();
1121+ Set <ChannelConversion > channelConversions = new LinkedHashSet <>();
10941122 for (TreeEdge edge : this .outEdges ) {
10951123 channelConversions .add (edge .channelConversion );
10961124 channelConversions .addAll (edge .destination .getChildChannelConversions ());
0 commit comments