possible bug in the for _ in 1...N idiom

https://stackoverflow.com/questions/63852086/swift-100-times-slower-than-c-in-simple-program
Answered by Claude31 in 632208022
Yes, what you did was interesting. But a bug report is even more useful.

Range evaluation seems to be the problem.

I added a loop with open range and surprisingly, loop is significantly faster:
Code Block
var t = 0
var start = CFAbsoluteTimeGetCurrent()
while t < 10_000_000 {
t += 1
}
print("while duration", CFAbsoluteTimeGetCurrent() - start)
start = CFAbsoluteTimeGetCurrent()
t = 0
for _ in 1...10_000_000 { // 10 mio iterations
t += 1
}
print("for duration", CFAbsoluteTimeGetCurrent() - start)
start = CFAbsoluteTimeGetCurrent()
t = 0
for _ in 1..<10_000_001 { // 10 mio iterations
t += 1
}
print("for duration open range", CFAbsoluteTimeGetCurrent() - start)
start = CFAbsoluteTimeGetCurrent()
t = 0
repeat {
t += 1
} while t < 10_000_000
print("repeat duration", CFAbsoluteTimeGetCurrent() - start)

Yields:
while duration 0.040472984313964844
for duration 6.2793790102005005
for duration open range 4.28174901008606
repeat duration 0.02440011501312256


I did post a bug report #FB8679385
Showing only a link to an external site seems a little bit rude. You should better include what you want to say about the link.
You already got many answers on SO. Isn't it enough ?
what is nice is to report a possible bug / strange behaviour where supposedly attention to it should be appreciated, for the benefit of all
at any rate, this does not require answers nor attention from people on stackoverflow, that much is sure. only, just maybe, possibly, it requires action by actual apple developers. you are very welcome.
changing the for-loop to a while-loop brings execution speed down from 13s to 0.4s

import Foundation

let start = CFAbsoluteTimeGetCurrent()

var t = 0
for _ in 1...10000000 { // 10 mio iterations
t += 1
}

print(t)
print(CFAbsoluteTimeGetCurrent() - start) // 13.17 seconds

please see more details here
https://stackoverflow.com/questions/63852086/swift-100-times-slower-than-c-in-simple-program
Where do you run ?
  • In playground (then, performance is not guaranteed at all)

  • on simulator ? I tested with Xcode 11.3 and got 6.3 s

  • on device ?

You are right, while is 300 times faster than for loop:
Code Block
var start = CFAbsoluteTimeGetCurrent()
var t = 0
while t < 10_000_000 {
t += 1
}
print("while duration", CFAbsoluteTimeGetCurrent() - start) // 0.0280 seconds
start = CFAbsoluteTimeGetCurrent()
t = 0
for _ in 1...10_000_000 { // 10 mio iterations
t += 1
}
print("for duration", CFAbsoluteTimeGetCurrent() - start) // 6.280 seconds


And repeat is even slightly faster (10% better than while)
Code Block
repeat {
t += 1
} while t < 10_000_000

That's worth a bug report probably.
this was build as a standard command line tool, no playground involved. run on a mac. please see post in stackoverflow, since every effort was already made there to explain this as succinct as possible, with all system and settings info provided, also different tests, mine and those of other people.
this is not me asking for help: in benefit of the general community, i have done my due duty reporting what i have seen, after having tested to the best of my ability within a limited time available.
The place to report bugs is feedbackassistant.apple.com. As this seems to be related to the swift implementation, the swift project has a bug tracker at bugs.swift.org
Accepted Answer
Yes, what you did was interesting. But a bug report is even more useful.

Range evaluation seems to be the problem.

I added a loop with open range and surprisingly, loop is significantly faster:
Code Block
var t = 0
var start = CFAbsoluteTimeGetCurrent()
while t < 10_000_000 {
t += 1
}
print("while duration", CFAbsoluteTimeGetCurrent() - start)
start = CFAbsoluteTimeGetCurrent()
t = 0
for _ in 1...10_000_000 { // 10 mio iterations
t += 1
}
print("for duration", CFAbsoluteTimeGetCurrent() - start)
start = CFAbsoluteTimeGetCurrent()
t = 0
for _ in 1..<10_000_001 { // 10 mio iterations
t += 1
}
print("for duration open range", CFAbsoluteTimeGetCurrent() - start)
start = CFAbsoluteTimeGetCurrent()
t = 0
repeat {
t += 1
} while t < 10_000_000
print("repeat duration", CFAbsoluteTimeGetCurrent() - start)

Yields:
while duration 0.040472984313964844
for duration 6.2793790102005005
for duration open range 4.28174901008606
repeat duration 0.02440011501312256


I did post a bug report #FB8679385
@Claude31 please if you may provide this as an answer on stackoverflow, you might want to request a reopening first though. i think the attitude on stackoverflow may benefit from this. benefit in the sense that people relax their prejudice as to posters intent, and in the sense that posters stay willing to post questions of this type. although the change it will make is close to nil, have to keep trying i guess. thanks for your testing.
Under the Release build exactly the same code shown by Claud31 produces this output:
Code Block
while duration 0.0
for duration 6.99758529663086e-05
for duration open range 7.009506225585938e-05
repeat duration 0.0001119375228881836


You should better consider the result in Optimized build.
This is not a bug, just a problem of optimization.
In my opinion, you are not getting the result under the right Release build settings.
  1. Create a new project: macOS/Command Line Tool

  2. Replace print("Hello, World!") in main.swift to the tested code

  3. Select Edit Scheme... from the pull down menu shown when clicking your project name right to the run/stop buttons.

  4. In the Edit Scheme sheet shown, choose ▶︎Run (in the left pane), info (in the top bar of the right pane)

  5. Choose Release for Build Configuration, and click Close

  6. Build & Run your code.

Never edit Build Settings directly. A slight mistake may cause unexpected behavior.

In the linked SO thread, there's a description:

technical details: for swift compilation, i disabled "safety checks" and all testing and debug capability in both debug and release configurations
...

Never do such things and just choose Release, and see what you get.


The following is my testing code I first used when I saw the SO thread:
Code Block
import Foundation
func test1() {
let start = CFAbsoluteTimeGetCurrent()
var t = 0
for _ in 1...10_000_000 {
t += 1
}
print(t)
print(CFAbsoluteTimeGetCurrent() - start) // 0.00033605098724365234
}
///What's behind the Swift for-in loop
func test2() {
let start = CFAbsoluteTimeGetCurrent()
var t = 0
var ite = (1...10_000_000).makeIterator()
while let _ = ite.next() {
t += 1
}
print(t)
print(CFAbsoluteTimeGetCurrent() - start) // 0.0002759695053100586
}
///Swift code equivalent to the C++ code
func test3() {
let start = CFAbsoluteTimeGetCurrent()
var t = 0
let s = (0..<1000).randomElement()
for _ in 1...10_000_000 {
t += 1
if t == s { print(t) }; // prevent optimising code out. do "real work"
}
print(t)
print(CFAbsoluteTimeGetCurrent() - start) // 0.002339005470275879
}
///Direct conversion of C-style for loop into Swift
func test4() {
let start = CFAbsoluteTimeGetCurrent()
var t = 0
let s = (0..<1000).randomElement()
var i = 0
while i < 10_000_000 {
defer {i += 1}
t += 1
if t == s { print(t) }; // prevent optimising code out. do "real work"
}
print(t)
print(CFAbsoluteTimeGetCurrent() - start) // 0.003098011016845703
}
test1()
test2()
test3()
test4()


If you want to insist that simple for-in loops over range should be faster even in non-optimized, it seems to be a reasonable opinion.

But if you want to discuss the performance under the optimized build, I guess you are mistaking to set the right build settings.
possible bug in the for _ in 1...N idiom
 
 
Q