Didn't originally do it, but added it and here are the results (in seconds):
-O none
guard: 27.8599720001221 (+0.08%)
if-continue: 29.0756570100784 (+4.00%)
where: 27.836905002594 (+0.00%)
filter: 46.8083620071411 (+68.15%)
lazy.filter: 33.3811990022659 (+19.91%)
-O fast
guard: 0.123715996742249 (+4.00%)
if-continue: 0.118164002895355 (+0.00%)
where: 0.118863999843597 (+0.59%)
filter: 0.520934045314789 (+40.85%)
lazy.filter: 0.132100999355316 (+11.79%)
Note that in order to prevent some compiler magic for ranges (not sure if there
is any), I've done the following:
let arr = Array(range)
Also, I invoke the block() within timetest 100 times to get larger values,
since there can be some minor changes in calling NSDate() - the first time it's
called, +initialize may get called as well as the IMP might not be cached and
with 0.04 seconds, it can make the result a bit off due to a lock during
initialization of NSDate.
Nevertheless my argument against removing where from the for loops is that:
1) Without boilterplate guard/if, the performance is poor.
2) With the boilerplate, you add 3 lines of code. I do not like the one-liner
if condition { continue }
3) I'd vote for keeping where next to the variable name as suggested by Brent.
> On Jun 9, 2016, at 4:22 PM, Erica Sadun <[email protected]> wrote:
>
> So how did the guard versions perform?
>
> Sent from my iPad
>
>> On Jun 9, 2016, at 4:27 AM, Charlie Monroe <[email protected]> wrote:
>>
>>
>>>> On Jun 9, 2016, at 10:29 AM, Brent Royal-Gordon <[email protected]>
>>>> wrote:
>>>>
>>>> I've taken the time to run a test, going through milion numbers (several
>>>> times) using:
>>>>
>>>> for i in arr { if i % 2 == 0 { continue } }
>>>> for i in arr where i % 2 == 0 { }
>>>> for i in arr.filter({ $0 % 2 == 0 }) { }
>>>> for i in arr.lazy.filter({ $0 % 2 == 0 }) { }
>>>>
>>>> Results:
>>>>
>>>> - plain for loop with if-continue: 27.19 seconds (+1.76%)
>>>> - with where: 26.72 seconds (+0.00%)
>>>> - .filter: 44.73 seconds (+67.40%)
>>>> - .lazy.filter: 31.66 seconds (+18.48%)
>>>
>>> This is great data. I have a hard time imagining a little compiler work
>>> couldn't make if-continue as fast as for-where, but lazy.filter might be a
>>> taller order for it, and optimizing plain filter could actually change
>>> behavior.
>>>
>>> A month or two ago, I actually fell into the "just use the higher-order
>>> functions" camp on this question, but I've been rethinking that more and
>>> more lately. Between the trailing closure incompatibility, the need to
>>> remember to use `lazy` to get decent performance, and now the noticeable
>>> speed difference even *with* lazy, I'm no longer convinced that answer is
>>> good enough.
>>
>> There will IMHO always be noticeable overhead since you're calling a
>> function which is then invoking a closure. When you look at what that means:
>>
>> - thunks generated around the invocation, which are a few instructions
>> - new stack frame for each call (correct me if I'm wrong).
>>
>> So instead of a single `i % 2 == 0` (which is just 2-3 instructions,
>> depending on the architecture and optimization settings), it will invoke the
>> closure milion times, if the array contains a milion members.
>>
>> Maybe I'm over-optimizing, but 18% seemed like a lot to me.
>>
>>
>>>
>>> (Though I do think `while` is probably too niche to bother with as a
>>> first-class feature, and I am open to if-continue on the `where` clause.)
>>>
>>> --
>>> Brent Royal-Gordon
>>> Architechies
>>
>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution