Improving Swift code for scalability

OK, how much impact do you think this patch could possibly have on our matter?

diff -r 631593d9f4fa -r 07055be695f9 AsyncCountdown/AsyncExplorers.swift
--- a/AsyncCountdown/AsyncExplorers.swift       Mon Jul 10 22:51:12 2023 +0200
+++ b/AsyncCountdown/AsyncExplorers.swift       Sat Sep 23 14:19:11 2023 +0200
@@ -143,8 +143,7 @@
                              selfNode);
             
             // close current composition
-            var num = 0;
-            if (selfNode({_,_ in guard num == 0 else {return ();}; num += 1; return nil;}) != nil)
+            if /* parent is */ (!emptyComposition)
                 && ( (kind == .additive) ? innerExploreAdditionOfRightNodeCtx.forwardValue > innerExploreAdditionOfRightNodeCtx.reverseValue :
                             ((innerExploreAdditionOfRightNodeCtx.forwardValue % innerExploreAdditionOfRightNodeCtx.reverseValue) == 0) ) {
                 

I did not think it could have a lot, myself, as I performed this change while in the middle of more significant rearrangements: the latter involved some changes to the signature of the selfNode parameter, and I considered it would be just be as well to reuse a cached result here rather than have to keep tinkering with the signature of an anonymous closure that does not use its own parameters anyway just to keep it in sync (if you don't feel up to reading how it works, just know this code makes sure an operation has at least two operands before we try to obtain its result). For reasons that will eventually become clear, I tested this patch in isolation, as directly applied to the without dictionaries case. What's going to be the outcome?

Swift without dictionaries and with reduced walker calls

 N|  T  | factor
 1|100  | ref
 2| 62.8|x1.59
 3| 52.1|x1.92
 4| 48.1|x2.07
 5| 41.6|x2.40
 6| 37.2|x2.69
 7| 34.3|x2.91
 8| 32.3|x3.10
 9| 32.2|x3.11
10| 32.1|x3.12
11| 32.1|x3.12
12| 31.7|x3.15

(No change in methodology)

discussion

For the first time I break x2 with just the performance cores, and x3 when adding the efficiency cores: now that is a significant improvement (absolute performance of the reference did also improve, in case you're curious). Now I have a pretty good idea on why that is, but I would like to hear your theories before I expose it. Why did this small change have such an effect?

Bonus: in a further patch I also removed the call to walkOperands that sets emptyComposition (using instead specially added state to track the number of operands so as to obtain the same result), but that change did not have a significant impact on scalability. Why is that, in your opinion?

1 Like