0

The code below shows two ways of building a spreadsheet : by using:

str = str + "\(number) ; "

or

str.append("\(number)");

Both are really slow because, I think, they discard both strings and make a third one which is the concatenation of the first two. Now, If I repeat this operation hundreds of thousands of times to grow a spreadsheet... that makes a lot of allocations.

For instance, the code below takes 11 seconds to execute on my MacBook Pro 2016:

    let start = Date()
    var str = "";

    for i in 0 ..< 86400
    {
        for j in 0 ..< 80
        {
//          Use either one, no difference
//          str = str + "\(Double(j) * 1.23456789086756 + Double(i)) ; "
            str.append("\(Double(j) * 1.23456789086756 + Double(i)) ; ");
        }

        str.append("\n")
    }

    let duration = Date().timeIntervalSinceReferenceDate - start.timeIntervalSinceReferenceDate;
    print(duration);

How can I solve this issue without having to convert the doubles to string myself ? I have been stuck on this for 3 days... my programming skills are pretty limited, as you can probably see from the code above...

I tried:

var str = NSMutableString(capacity: 86400*80*20);

but the compiler tells me:

Variable 'str' was never mutated; consider changing to 'let' constant

despite the

str.append("\(Double(j) * 1.23456789086756 + Double(i)) ; ");

So apparently, calling append does not mutate the string...

Adeline
  • 465
  • 5
  • 16
  • 5
    I don't think `str.append` or `Double` is slow... calling a line of nested code `86400 * 80 = 6,912,000` times would make anything seem slow. – Michael Dautermann Jul 24 '16 at 11:17
  • Yes I know that. And it is because I make 6,912,000 a string grow... so I make copies, allocations, etc... remove that part (no addition, just create a new string) and see how blazing fast it runs (0.043 sec). The allocation are the killer here. so I tried making it an NSMutableString with capacity, but apparently, in swift3 append copies instead of mutating. – Adeline Jul 24 '16 at 11:31
  • With optimization enabled, "no addition, just create a new string" code would be removed. How much time does your code take for appending constant string, like `str.append("1.23456789086756 ; ")` ? – OOPer Jul 24 '16 at 11:46
  • I'm guessing you want to write this to a file or something. If so just create a stream and write the value to the stream. It will just append the value to the current stream position and you won't have to worry about str.append. You may also find it quicker to replace the * with + if that's part of the final calculation - though not sure these days, have to test it – daven11 Jul 24 '16 at 12:19
  • It seems the problematic part is the conversion from `Double` to `String`, everything else is pretty fast in my testing. – Sulthan Jul 24 '16 at 12:48
  • @Sulthan yes, there is a bottleneck here. Could it be the memory allocation rather than the conversion itself ? – Adeline Jul 24 '16 at 13:28
  • @daven11 I would have to change all the data processing scheme, because the spreadsheet is built in some unit of the program, the handed to a grapher (which will decide where to write it). The same data can be handed to several graphers, hence there is an optimization in building before knowing the filepath. And while that seems to be a waste of diskspace having several files containing identical data, there is a scientific reason behind it. – Adeline Jul 24 '16 at 13:30
  • Don't use string interpolation (`"\(this)"`) solely for type conversion. Either do: `"\(str)\(number) ; "` or `str + String(number)`. Don't mix and match. – Alexander Jul 24 '16 at 14:52
  • @Adeline if you'd like to keep it in memory then use a memory stream (an NSOutputStream for memory consult the Stream Programming Guide) this may get you started http://stackoverflow.com/questions/26331636/writing-a-string-to-an-nsoutputstream-in-swift – daven11 Jul 24 '16 at 22:51

1 Answers1

2

I tried writing it to an array and the limiting factor seems to be the conversion of a double to a string.

The code below takes 13 seconds or so on my air

doing this

arr[i][j] = "1.23456789086756"

drops the execution time to 2 seconds so 11 seconds is taken up in converting Double to String. You might be able to shave off some time by writing your own conversion routine but that seems the limiting factor. I tried using memory streams and that seems even slower.

 var start = Date()

        var arr = Array(repeating: Array(repeating: "1.23456789086756", count: 80), count: 86400 )
        var duration = Date().timeIntervalSinceReferenceDate - start.timeIntervalSinceReferenceDate;
        print(duration); //0.007
        start = Date()

        var a = 1.23456789086756

        for i in 0 ..< 86400
        {
            for j in 0 ..< 80
            {
                arr[i][j] = "\(a)" // "1.23456789086756" //String(a)
            }
        }

        duration = Date().timeIntervalSinceReferenceDate - start.timeIntervalSinceReferenceDate;
        print(duration); //13.46 or 2.3 with the string
daven11
  • 2,905
  • 3
  • 26
  • 43
  • +1 – thanks, even if I am not trying to fill an array. I'll wait a little more and if there are no more replies, I will accept yours as the closest answer to the question. – Adeline Jul 30 '16 at 16:56
  • @Adeline one way to trim it down is to parallelise the routine - probably obvious but mentioned it just in case, pretty simple to do in this case. If you have a quad cores you could expect to get it down to a few seconds – daven11 Jul 30 '16 at 22:28