Search code examples
algorithmswift3graph-theoryfloyd-warshall

Floyd-Warshall performance on Swift 3


I'm relatively new to Swift so I have probably done something dumb.

I've implemented code very similar to this in Java and the algorithm executes in under 1 second on a graph with 800 vertices. However the Swift 3 version takes over 5 minutes on the same graph!
What have I done wrong?

var myMatrix = myGraph.getAdjMatr()   //  Returns adjacency matrix as 2d array.
let n = myGraph.vertexCount!

for i in 0..<n
{
    for j in 0..<n
    {
        if (myMatrix[i][j] == 0)
        {
            myMatrix[i][j] = Int.max/2
        }
    }
}

for i in 0..<n
{
    myMatrix[i][i] = 0;
}

for i in 0..<n
{
    for j in 0..<n
    {
        for k in 0..<n
        {
            myMatrix[j][k] = min(myMatrix[j][k], (myMatrix[i][k] + myMatrix[j][i]))
        }
    }
}

Thanks.


Solution

  • I'm relatively new to Swift so I have probably done something dumb.

    You've done nothing dumb, but performance of Swift can be "interesting". Let's have a play and see what we find, none of this is "definitive", but hopefully it will help. All tests done with Xcode 8/Swift 3, and an 800 node graph forming a single directional circle, YMMV.

    Instead of comparing against Java I coded your algorithm in C wrapped as Objective-C i.e. using standard C value arrays. This can be directly called from Swift and provides a baseline for best speed.

    Next I compared your Swift code above using a Swift 2-dimensional array of Int. The performance was about 80 times slower than the (Objective-)C version. Ouch.

    Finally I replaced the 2-dimensional array with a single-dimensional one and did the index calculation direct in the code, i.e. replacing:

    myMatrix[i][j]
    

    with:

    myMatrix[i*n + j]
    

    This version is about 15 times slower than the baseline. Better (relatively!). This improvement will be down to a 2-dimensional array in Swift actually being a 1-dimensional array of 1-dimensional arrays.

    The above tests were done in normal debug mode, i.e. without optimisation. With fast optimisation the C baseline was roughly 4 times faster but the Swift versions changed only slightly. So with optimisation on the Swift 1-dimensional version was about 60 times slower than the C baseline version.

    Your reported ratio of Swift to Java is about 300:1, far worse than any of my results. Where they run on the same machine? Also if your array is actually an NSArray that may account for the difference, but I haven't run tests to check this hypothesis (an NSArray is object-based and somewhat slower (but more flexible) than a C array).

    Results (YMMV):

    1. Avoid Swift multi-dimensional arrays, map them onto 1-dimensional arrays doing the index calculations yourself.
    2. Use C arrays if you can. Requires calling (Objective-)C code from Swift which is straightforward but you need to know (Objective-)C of course!

    HTH