Skip to content

Commit d7ae8f0

Browse files
committed
finished writing issue 13
1 parent c5d8931 commit d7ae8f0

File tree

3 files changed

+284
-11
lines changed

3 files changed

+284
-11
lines changed

issue13/README.md

Lines changed: 217 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,40 @@ class HighPriority {
269269
}
270270
```
271271

272-
TO BE CONTINUED
272+
I want to now do a test on the traditional `for` loop and run it with the `HighPriority` class which we just wrote, on an iPhone 7 with full optimization on the code, and see how long it takes. But since the code right now includes a `printf()` function, it will take a huge amount of time to execute. That's dumb, so let's remove the `printf()` and make the code smarter so that it won't get optimized out since it's not doing anything, but still do something unnecessarily enough for the code to be executable:
273+
274+
```swift
275+
@inline(never)
276+
func traditionalForLoop() {
277+
278+
var lastValue = 0
279+
for value in 0...0xDEADBEEF {
280+
if value % 0xDEED == 0 {
281+
lastValue = value
282+
}
283+
}
284+
if lastValue < 0 {
285+
print(lastValue)
286+
}
287+
288+
}
289+
```
290+
291+
If I run this code with our `HighPriority` class as shown here:
292+
293+
```swift
294+
HighPriority(task: traditionalForLoop).perform { (time) in
295+
print(time)
296+
}
297+
```
298+
299+
I get the following result: **2.68 seconds**
300+
301+
Verdict:
302+
303+
| | Aesthetics | Conciseness | Performance | Overal Score |
304+
|------------|------------|-------------|-------------|--------------|
305+
| `for` loop | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ |
273306

274307

275308
`forEach{}` Loop
@@ -372,22 +405,204 @@ Let's look at the generated assembly code to get some hints as to how this loop
372405
0x00000001000061bc C0035FD6 ret
373406
```
374407

408+
So it appears as if the call to the `forEach` function of the array was made inline and this code is very much identical to the normal `for` loop.
375409

410+
I want to do the same thing with this code and run it through the `HighPriority` class and see how long it takes to execute on an iPhone 7 with full optimization turned on but we have to also make this code smarter so that it doesn't include a `printf()` statement (since that takes a huge amount of time for a loop between `0...0xDEADBEEF`) and also we need to ensure that the code won't just do nothing, since it will get optimized out. So here is the new code:
376411

412+
```swift
413+
@inline(never)
414+
func forEachLoop() {
415+
416+
var lastValue = 0
417+
(0...0xDEADBEEF).forEach {value in
418+
if value % 2 == 0 {
419+
lastValue = value
420+
}
421+
}
422+
if lastValue < 0 {
423+
print(lastValue)
424+
}
425+
426+
}
427+
```
377428

429+
I get the following result: **2.48 seconds**
378430

431+
Verdict:
379432

433+
| | Aesthetics | Conciseness | Performance | Overal Score |
434+
|----------------|------------|-------------|-------------|--------------|
435+
| `forEach` loop | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ |
380436

381437

438+
`while` Loop
439+
===
440+
I don't think any programmer is ever going to write their code like this but as of the time of writing this article, you can create a loop simply by using the `while` statement. I have very few examples where a `while` would be a useful way of doing a loop, especially through numbers, but for the sake of wholesomeness of this article, let's have a look at a `while` loop as well:
382441

442+
```swift
443+
@inline(never)
444+
func whileLoop() {
445+
446+
var value = 0
447+
var lastValue = 0
448+
while value < 0xDEADBEEF {
449+
if value % 2 == 0 {
450+
lastValue = value
451+
}
452+
value += 1
453+
}
454+
if lastValue < 0 {
455+
print(lastValue)
456+
}
457+
458+
}
459+
```
460+
461+
Let's check out the ARM asm source as well:
462+
463+
```asm
464+
0x00000001000098f8 FF0301D1 sub sp, sp, #0x40
465+
0x00000001000098fc F65701A9 stp x22, x21, [sp, #0x10]
466+
0x0000000100009900 F44F02A9 stp x20, x19, [sp, #0x20]
467+
0x0000000100009904 FD7B03A9 stp x29, x30, [sp, #0x30]
468+
0x0000000100009908 FDC30091 add x29, sp, #0x30
469+
0x000000010000990c 080080D2 movz x8, #0x0
470+
0x0000000100009910 140080D2 movz x20, #0x0
471+
0x0000000100009914 A9D5BB52 movz w9, #0xdead, lsl #16
472+
0x0000000100009918 E9DD9772 movk w9, #0xbeef
473+
474+
0x000000010000991c 1F0140F2 tst x8, #0x1 ; XREF=__T012swift_weekly11AppDelegateC9whileLoopyyF+52
475+
0x0000000100009920 1401949A csel x20, x8, x20, eq
476+
0x0000000100009924 08050091 add x8, x8, #0x1
477+
0x0000000100009928 1F0109EB cmp x8, x9
478+
0x000000010000992c 8BFFFF54 b.lt 0x10000991c
479+
480+
0x0000000100009930 7405F8B6 tbz x20, #0x3f, 0x1000099dc
481+
482+
0x0000000100009934 1F2003D5 nop
483+
0x0000000100009938 00FF0158 ldr x0, #0x10000d918
484+
0x000000010000993c 000200B5 cbnz x0, 0x10000997c
485+
486+
0x0000000100009940 1F2003D5 nop
487+
0x0000000100009944 E0FE0158 ldr x0, #0x10000d920
488+
0x0000000100009948 200100B5 cbnz x0, 0x10000996c
489+
490+
0x000000010000994c E0030032 orr w0, wzr, #0x1
491+
0x0000000100009950 E3230091 add x3, sp, #0x8
492+
0x0000000100009954 010080D2 movz x1, #0x0
493+
0x0000000100009958 020080D2 movz x2, #0x0 ; argument #1 for method _swift_rt_swift_getExistentialTypeMetadata
494+
0x000000010000995c 5E010094 bl _swift_rt_swift_getExistentialTypeMetadata
495+
0x0000000100009960 08FE0110 adr x8, #0x10000d920
496+
0x0000000100009964 1F2003D5 nop
497+
0x0000000100009968 00FD9FC8 stlr x0, [x8]
498+
499+
0x000000010000996c B9010094 bl imp___stubs___T0s23_ContiguousArrayStorageCMa ; XREF=__T012swift_weekly11AppDelegateC9whileLoopyyF+80
500+
0x0000000100009970 48FD0110 adr x8, #0x10000d918
501+
0x0000000100009974 1F2003D5 nop
502+
0x0000000100009978 00FD9FC8 stlr x0, [x8]
503+
504+
0x000000010000997c E1031A32 orr w1, wzr, #0x40 ; XREF=__T012swift_weekly11AppDelegateC9whileLoopyyF+68
505+
0x0000000100009980 E20B0032 orr w2, wzr, #0x7
506+
0x0000000100009984 A1FDFF97 bl _swift_rt_swift_allocObject
507+
0x0000000100009988 F30300AA mov x19, x0
508+
0x000000010000998c E8030032 orr w8, wzr, #0x1
509+
0x0000000100009990 E9031F32 orr w9, wzr, #0x2
510+
0x0000000100009994 682601A9 stp x8, x9, [x19, #0x10]
511+
0x0000000100009998 1F2003D5 nop
512+
0x000000010000999c E8330158 ldr x8, #0x10000c018
513+
0x00000001000099a0 681E00F9 str x8, [x19, #0x38]
514+
0x00000001000099a4 741200F9 str x20, [x19, #0x20]
515+
0x00000001000099a8 B0010094 bl imp___stubs___T0s5printySayypGd_SS9separatorSS10terminatortFfA0_
516+
0x00000001000099ac F40300AA mov x20, x0
517+
0x00000001000099b0 F50301AA mov x21, x1
518+
0x00000001000099b4 F60302AA mov x22, x2
519+
0x00000001000099b8 AF010094 bl imp___stubs___T0s5printySayypGd_SS9separatorSS10terminatortFfA1_
520+
0x00000001000099bc E40300AA mov x4, x0
521+
0x00000001000099c0 E50301AA mov x5, x1
522+
0x00000001000099c4 E60302AA mov x6, x2
523+
0x00000001000099c8 E00313AA mov x0, x19
524+
0x00000001000099cc E10314AA mov x1, x20
525+
0x00000001000099d0 E20315AA mov x2, x21
526+
0x00000001000099d4 E30316AA mov x3, x22
527+
0x00000001000099d8 A1010094 bl imp___stubs___T0s5printySayypGd_SS9separatorSS10terminatortF
528+
529+
0x00000001000099dc FD7B43A9 ldp x29, x30, [sp, #0x30] ; XREF=__T012swift_weekly11AppDelegateC9whileLoopyyF+56
530+
0x00000001000099e0 F44F42A9 ldp x20, x19, [sp, #0x20]
531+
0x00000001000099e4 F65741A9 ldp x22, x21, [sp, #0x10]
532+
0x00000001000099e8 FF030191 add sp, sp, #0x40
533+
0x00000001000099ec C0035FD6 ret
534+
```
535+
536+
The generated assembly code for this `while` loop is very similar to that generated for the `forEach` statement and that on its own very similar to the `for` loop. So as far as the asm code is concerned you can be certain that the compiler is doing a hell of a job! But what about performance. I am going to put this into our `HighPriority` class and run it with full optimization on an iPhone 7:
537+
538+
```swift
539+
HighPriority(task: whileLoop).perform { (time) in
540+
print(time)
541+
}
542+
```
543+
544+
And I am receiving the following score: **2.45 seconds**
545+
546+
Verdict:
547+
548+
| | Aesthetics | Conciseness | Performance | Overal Score |
549+
|--------------|------------|-------------|-------------|--------------|
550+
| `while` loop | ⭐️⭐️⭐️ | ⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️ |
383551

552+
The reason behind the low score for aesthetics and conciseness are co-related. The `while` statement is physically more syntax to write and hence takes longer to understand than a simple for-loop for a finite task such as going through an array.
384553

385554

555+
`repeat...while` Loop
556+
===
557+
This type of loop is very similar to the `while` loop which i talked about earlier with the only difference being that this loop puts the condition at the end of the loop. So you can say that the loop runs at least once and then checks for its condition to exit or continue.
558+
559+
```swift
560+
@inline(never)
561+
func repeatLoop() {
562+
563+
var value = 0
564+
var lastValue = 0
565+
566+
repeat {
567+
if value % 2 == 0 {
568+
lastValue = value
569+
}
570+
value += 1
571+
} while value < 0xDEADBEEF
572+
573+
if lastValue < 0 {
574+
print(lastValue)
575+
}
576+
577+
}
578+
```
386579

580+
I won't go through the ARM assembly code for this type of loop since that's not one of the measurements in our verdicts and I didn't really go into the details of the other ARM assembly outputs either so it's pointless for the purpose of this article. Let's run this code with full optimization with our `HighPriority` class on an iPhone 7 and see the results:
387581

582+
```swift
583+
HighPriority(task: repeatLoop).perform { (time) in
584+
print(time)
585+
}
586+
```
587+
588+
And I'm receiving the following value: **2.49 seconds**
388589

590+
Verdict:
389591

592+
| | Aesthetics | Conciseness | Performance | Overal Score |
593+
|-----------------------|------------|-------------|-------------|--------------|
594+
| `repeat...while` loop | ⭐️⭐️⭐️ | ⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️ |
390595

596+
I scored the aesthetics and the conciseness categories of the `while` loop lower than the traditional `for` loop since it looks disgusting in my opinion and it also puts the condition of the loop at the end, which makes it less readable. So let's gather all the data:
391597

392-
References
598+
Final Verdict
393599
===
600+
601+
Here are the contestants and their verdicts, **sorted by overal score**:
602+
603+
| | Aesthetics | Conciseness | Performance | Overal Score |
604+
|-----------------------|------------|-------------|-------------|--------------|
605+
| `for` loop | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ |
606+
| `forEach` loop | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ |
607+
| `repeat...while` loop | ⭐️⭐️⭐️ | ⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️ |
608+
| `while` loop | ⭐️⭐️⭐️ | ⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️ |

issue13/exampleCode/swift-weekly/AppDelegate.swift

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,87 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1616
@inline(never)
1717
func traditionalForLoop() {
1818

19+
var lastValue = 0
1920
for value in 0...0xDEADBEEF {
2021
if value % 0xDEED == 0 {
21-
print(value)
22+
lastValue = value
2223
}
2324
}
25+
if lastValue < 0 {
26+
print(lastValue)
27+
}
2428

2529
}
2630

27-
@inline(never)
28-
func forEachLoop() {
31+
@inline(never)
32+
func forEachLoop() {
33+
34+
var lastValue = 0
35+
(0...0xDEADBEEF).forEach {value in
36+
if value % 2 == 0 {
37+
lastValue = value
38+
}
39+
}
40+
if lastValue < 0 {
41+
print(lastValue)
42+
}
43+
44+
}
2945

30-
(0...0xDEADBEEF).forEach {value in
31-
if value % 2 == 0 {
32-
print(value)
46+
@inline(never)
47+
func whileLoop() {
48+
49+
var value = 0
50+
var lastValue = 0
51+
while value < 0xDEADBEEF {
52+
if value % 2 == 0 {
53+
lastValue = value
54+
}
55+
value += 1
56+
}
57+
if lastValue < 0 {
58+
print(lastValue)
3359
}
60+
3461
}
3562

36-
}
63+
@inline(never)
64+
func repeatLoop() {
65+
66+
var value = 0
67+
var lastValue = 0
68+
69+
repeat {
70+
if value % 2 == 0 {
71+
lastValue = value
72+
}
73+
value += 1
74+
} while value < 0xDEADBEEF
75+
76+
if lastValue < 0 {
77+
print(lastValue)
78+
}
79+
80+
}
3781

3882
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
3983
// Override point for customization after application launch.
40-
traditionalForLoop()
41-
forEachLoop()
84+
85+
// HighPriority(task: traditionalForLoop).perform { (time) in
86+
// print(time)
87+
// }
88+
//
89+
// HighPriority(task: forEachLoop).perform { (time) in
90+
// print(time)
91+
// }
92+
//
93+
// traditionalForLoop()
94+
// forEachLoop()
95+
96+
HighPriority(task: repeatLoop).perform { (time) in
97+
print(time)
98+
}
99+
42100
return true
43101
}
44102

0 commit comments

Comments
 (0)