14

Is there a way to increase execution speed of a playground? I want to iterate many cycles and not to wait 10 minutes.

For example:

import UIKit

var count = 0
for var i = 0; i < 1000000000; i++ {
    count++
}

This code will execute a way too long. But I want to get quick result.

Artem Shmatkov
  • 1,434
  • 5
  • 22
  • 41
  • Perhaps an optimizing compiler would reduce that code to: `var count = 1000000000`. – zaph Sep 27 '14 at 09:35
  • @Zaph Try to paste this code to playground and you will see what I mean. – Artem Shmatkov Sep 27 '14 at 09:41
  • 3
    The insane close-voting on this site is really becoming a problem. Great question and thanks for posting it. – Fattie Sep 27 '14 at 09:50
  • @Zaph - you surely understand this is typical example test code??? Zakh - thanks again for raising this – Fattie Sep 27 '14 at 09:51
  • 4
    I would suggest that you simply shouldn't be doing things like that in a playground. I feel Apple intend playgrounds to be for testing out little snippets and ideas, not for running *anything* a billion times. As the code is all re-executed when you edit it, and has live display of values, etc., I can't really see that anyone would be able to make it run fast enough to do anything a billion times, even something quite trivial. As others have observed, an optimising compiler would simply kill your loop, and many other features of optimisers would make the "live view" of a playground impossible. – Matt Gibson Sep 27 '14 at 11:32
  • Sure, it is example code that has no relation to real world applications and the playground is not a shipping configuration. What ships is with a release optimization, try that. @Joe, I agree with the close voting. – zaph Sep 27 '14 at 21:09
  • The new playgrounds have "Sources" folder, were you can put your Swift files that will be complied once and made available from Playground. This will speed up the playgrounds – Kostiantyn Koval May 17 '15 at 15:11
  • 2
    I was using Playgrounds not to measure performance but to do some calculation. The question is a valid one. – Kartick Vaddadi Aug 07 '17 at 08:45
  • Putting files in the source folder did not work for me to improve performance in my use case, which involves executing a draw loop from within playgrounds. The only thing that works is the 'ugly code' method.' I do hope Apple gives us a way of disabling output/counter in Playgrounds that does the same thing as wrapping everything in a tuple. – rikitikitavi Aug 20 '21 at 19:37

4 Answers4

9

One of the biggest performance killer is the output at the right side of the playground. Now I will show you how to minimize this output.

See at the end for your example code.


Best Performance

The most performant way is to make all the performance critical code in a .swift file inside the Sources folder in the playground.

Note: In order to use the functions, classes, properties and methods from the Sources folder you have to mark them public. If you want to subclass a class it has to be marked open.


Good Performance but ugly code

The following method (I think this is not official/intended) can be used to disable the playground output but also leads to ugly code. However it is good for temporary disabling the output.

There are two main ways (and two tricks) to achieve the minimum amount of output (If you find a better way let us know):

  1. Use parenthesis around Void (or Void?) expressions like assignments (normally leads to no output, see also 3.).

    var x = 0       // output: 0
    (x = 1)         // NO output
    (x = 2 * x - 1) // NO output
    (x.negate())    // NO output
    

    Note: In Swift an assignment returns Void and in case of optional chaining it is Void?.

    var x: (Int, Int)? = nil
    if (x?.0 = 0) != nil {
        // assignment was successful (x!=0 and now x=(0, x.1))
    } else {
        // assignment was not successful (x==nil)
    }
    
  2. Initialize and declare variables separately.

    var x: Int // NO output
    (x = 0)    // NO output
    
  3. If 1. does not work add an additional no-op (no operation) line above or below ().

    This happens in single line closures (and probably in some other contexts) for example: (see also the code below)

    [1, 4, 5, 6].mmap{
        () // without this line the line below would yield to an output
        ($1 = $0 + 1)
    } as [Int]
    
  4. Instead of wrapping every line in parenthesis you can also use a tuple of all the expressions which is then assigned to a variable:

    var a: Any // this may be a useful definition in this context
    var x: Int
    var y: Int
    (a = (x = 0,
          y = 1,
          x = y + 1,
          y = x*x))
    

    However this could lead to a indentation disaster...

Where it does not work (I've found no way how to remove the output; This list is probably not complete):

  1. returns in functions and closures
  2. Declaration of Optional variables e.g.: var x: Int?

An example of a new map method on Sequence

Usage: See above at Point 3.

The signature of Sequence.map is

func map<T>(_ transform: (Self.Element) throws -> T) rethrows -> [T]

Since I have not found a way how to remove the output of returns one can use a closure with an inout argument (get the "return" value with an assignment). A possible signature could then be:

func mmap<U>(_ transform: (Element, inout U?) -> ()) -> [U]

so we can pass nil in the inout argument since it is a good default for every possible U without imposing a constraint on U which could require an instance generator (e.g.: init() { ... }).

Unfortunately Swfit has a hard time to infer U so you would need to help the compiler with explicit type annotations. In addition var newElement: U? does return nil in the sidebar.

Now I will use Any instead of U?:

extension Sequence {
    // ATTENTION: this is not as performant as the normal `map`!
    func mmap<U>(transform: (Element, inout Any) -> ()) -> [U] {
        var result: [U]
        (result = [U]())
        for element in self {
            var newElement: Any
            (newElement = 0) // some placeholder element
            (transform(element, &newElement))
            // assume the inout element to be of type `U`
            (result.append(newElement as! U))
        }
        return result // the ONLY output in this method
    }
}

Your example code

Using Swift 4

var count = 0
for i in 0..<1_000_000_000 {
    (count += 1)
    if count % 100_000 == 0 {
        // print only every 100_000th loop iteration
        print(count)
    }
}

Without the parenthesis: about 10.000 loop iterations per second

With parenthesis: about 10.000.000 loop iterations per second !!!

Qbyte
  • 12,753
  • 4
  • 41
  • 57
5

I feel your pain, I was playing around with printing 2D functions to [Double] then converting to UIImageView. One of the steps was iterating over millions of pixels, and it took forever.

Anything computationally intensive, or repetitive, or potentially time consuming should be put in the "Sources" folder of the playground. That way the code is precompiled before your playground is run. Put the output of that for loop in a public function that you can call from the playground. Then you won't have to sit there watching the playground count all the times it went through the for loop.

dsgrnt
  • 393
  • 2
  • 11
0

I had a such problem and I have solved it after days of trials. All I needed to do is move all my code in folder Sources of playground. So, after that the execution speed was enhanced. I hope it helps you.

Note: don't forget to use open classes.

0

To speed up the build in Xcode Playground and prevent the loading icon to keep spinning forever:

  1. go to the sidebar on the right, and change iOS to macOS in Playground Settings
  2. instead of importing UIKit, import Foundation (this would work if you're not trying to use specific stuff from the UIKit framework)
Kelvin Jou
  • 271
  • 2
  • 12