1

I have tried many things already, changing up a few things wrote in Swift differently, alternatively using a for instead of the built in 'contains' in swift to see if that might be the problem but Objective-C was still faster.

Objective-C code runs at about - 0.035266s. Swift - around 0.987877011299133s.

Can you guys help me find out what might be wrong with the code? I tried to write both codes identical line for line as much as it was possible, or used the languages given substitutes.

Swift main:

    import Foundation

let startDate = NSDate();

let cc = CaesarCipher();


let path = "/Users/Jay/Documents/Projects/CaesarCipher/CaesarCipherSwift/data.txt"
var content = String(contentsOfFile:path, encoding: NSUTF8StringEncoding, error: nil)

content = content!.lowercaseString;

let arr = cc.cipher(content!, shift: 13);

let endDate = NSDate();

let time = NSTimeInterval(endDate .timeIntervalSinceDate(startDate));

println("ciphered text: \n \(arr) \n");
println("execution time: \n \(time) \n");

Swift CaeserCipher:

import Foundation

class CaesarCipher
{
    let alphabet: [Character];
    var textToCipher: String;
    var shift: Int;

    init(){

        self.alphabet = ["a", "b", "c", "d", "e", "f", "g",
            "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z"];

        self.textToCipher = String();
        self.shift = Int();
    }

    func cipher(textToCipher: String, shift: Int)->String{

        self.textToCipher = textToCipher;
        self.shift = shift;
        var newArray: [Character] = [];

        for letter in self.textToCipher{

            if(contains(alphabet, letter))
            {

                var modValue = self.shift % 26;
                var indexOfLetter = find(alphabet, letter)!;

                if(modValue > indexOfLetter){

                    modValue = modValue-indexOfLetter;
                    newArray.append(alphabet[alphabet.count-modValue]);
                }else{
                    newArray.append(alphabet[indexOfLetter-modValue]);
                }

            }else{
                newArray.append(" ");
            }

        }

        return String(newArray);

    }
}

Objective-C main:

#import <Foundation/Foundation.h>
#import "CaesarCipher.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDate *methodStart = [NSDate date];

        CaesarCipher *cc = [[CaesarCipher alloc] init];
        NSString *path = @"/Users/Jay/Documents/Projects/CaesarCipher/CaesarCipherObjectiveC/data.txt";
        NSString *textToCipher =
        [[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil] lowercaseString];

        NSString *cipherText = [cc cipher:textToCipher :13];

        NSDate *methodFinish = [NSDate date];

        NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];

        NSLog(@"%@", cipherText);
        NSLog(@"executionTime = %f", executionTime);
    }
    return 0;
}

Objective-C CaeserCipher.m:

#import <Foundation/Foundation.h>
#import "CaesarCipher.h"

@implementation CaesarCipher
{
    NSArray *alphabet;
    NSMutableArray *cipherTextArray;
}
-(id)init{

    self = [super init];
    if(self){

        alphabet = [NSArray arrayWithObjects:@"a", @"b", @"c", @"d", @"e", @"f", @"g",
                    @"h", @"i", @"j", @"k", @"l", @"m", @"n", @"o", @"p", @"q", @"r", @"s",
                    @"t", @"u", @"v", @"w", @"x", @"y", @"z", nil];

    }
    return self;
}

-(NSString *)cipher:(NSString *)textToCipher :(int)shift
{

    NSMutableString *text;
    text = [NSMutableString string];

    for(int i=0; i<textToCipher.length; i++){

        NSString *currentLetter =
        [NSString stringWithFormat:@"%c", [textToCipher characterAtIndex:i]];

        if([alphabet containsObject:currentLetter]){

            int modValue = shift % 26;
            int indexOfLetter = (int)[alphabet indexOfObject:currentLetter];

            if(modValue > indexOfLetter){

                modValue = modValue - indexOfLetter;
                [text appendString:[alphabet objectAtIndex:(alphabet.count-modValue)]];
            }else{

                [text appendString:[alphabet objectAtIndex:(indexOfLetter-modValue)]];
            }

        }else{

            [text appendString:@" "];
        }

    }

    return text;
}

@end

Objective-C CaeserCipher.h:

@interface CaesarCipher: NSObject

@property int numerator, denominator;
@property NSString *str;

-(NSString *) cipher: (NSString *) textToCipher: (int) shift;


@end
ab1428x
  • 794
  • 2
  • 8
  • 20
  • Did you turn on optimisations for the Swift code? Unlike Objective-C, Swift is _very_ sensitive to compiler optimisations. – gnasher729 Apr 04 '15 at 13:23
  • A few [*random pauses*](http://stackoverflow.com/a/378024/23771) will show you why the time is being spent. No matter how swift Swift is, or any compiled language, you can easily bring it to its knees with library calls. Also, by the way, timing figures beyond two decimal places are meaningless, i.e. 0.987877011299133s is basically 1 second. – Mike Dunlavey Apr 04 '15 at 13:47
  • I agree but when the data file is bigger and I also add deciphering immediately after the cipher function, it becomes significant – ab1428x Apr 04 '15 at 15:46

2 Answers2

3

I was curious about Swift performance vs Objective-C and so I wrote a program to compare the two.

You can read about it here:

Testing Swift's performance against C/Objective-C

It turns out that Swift code performs pretty poorly with compiler optimization turned off (which is the standard setting for debug builds.) You really need to switch to release build in order to see what your code is going to perform like in the app store.

I don't recommend changing the compiler settings for debug builds because if you do that source debugging becomes difficult. (The compiler can optimize away local variables, it can change the order of execution or blend multiple source lines into one, etc.)

Try creating a build scheme that builds a release build and time THAT. I bet you'll find it runs much faster.

You can also switch the build settings on the release build to turn off bounds checking on arrays. That speeds things up, but it's kinda dangerous, and makes debugging harder.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • thanks for the answer. I'll be sure to check out the article, however in this particular situation, switching to release didn't make any difference. – ab1428x Apr 04 '15 at 12:04
  • Clean and rebuild to be sure all your modules get recompiled. It should make at least some difference. – Duncan C Apr 04 '15 at 12:07
1

This test is a combination of Swift language + Swift standard library against a combination of Objective-C language + Cocoa library.

Let's try with Swift language + Swift standard library against Swift language + Cocoa library:

class SWCaesarCipher {  // same code as yours, but with Cocoa methods instead of Swift standard library ones
    let alphabet: NSArray
    var textToCipher: NSString
    var shift: Int

    init(){

        self.alphabet = NSArray(objects: "a", "b", "c", "d", "e", "f", "g",
            "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z")

        self.textToCipher = NSString()
        self.shift = Int()
    }

    func cipher(textToCipher: NSString, shift: Int) -> NSString {

        self.textToCipher = textToCipher
        self.shift = shift
        var text = NSMutableString()

        var i = 0
        while i < self.textToCipher.length {
            var letter = NSString(format: "%c", textToCipher.characterAtIndex(i))

            if alphabet.containsObject(letter) {

                var modValue = self.shift % 26
                var indexOfLetter = alphabet.indexOfObject(letter)


                if (modValue > indexOfLetter) {

                    modValue = modValue - indexOfLetter

                    text.appendString(alphabet.objectAtIndex(alphabet.count - modValue) as! String)
                }else{
                    text.appendString(alphabet.objectAtIndex(indexOfLetter-modValue) as! String)
                }

            }else{
                text.appendString(" ")
            }

            i++

        }

        return text

    }
}

var startDate = NSDate()

let cc = SWCaesarCipher()

var content = NSString(string: "Black malt berliner weisse, filter. Ibu degrees plato alcohol. ipa hard cider ester infusion conditioning tank. Dry stout bottom fermenting yeast wort chiller wort chiller lager hand pump ! All-malt dunkle bright beer grainy, original gravity wheat beer glass.")

content = content.lowercaseString
let arr = cc.cipher(content, shift: 13)
var endDate = NSDate()
var time = NSTimeInterval(endDate .timeIntervalSinceDate(startDate))
println("ciphered text: \n \(arr) \n")
println("execution time: \n \(time) \n")

// your original code
class OPCaesarCipher
{
    let alphabet: [Character];
    var textToCipher: String;
    var shift: Int;

    init(){

        self.alphabet = ["a", "b", "c", "d", "e", "f", "g",
            "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z"];

        self.textToCipher = String();
        self.shift = Int();
    }

    func cipher(textToCipher: String, shift: Int)->String{

        self.textToCipher = textToCipher;
        self.shift = shift;
        var newArray: [Character] = [];

        for letter in self.textToCipher{

            if(contains(alphabet, letter))
            {

                var modValue = self.shift % 26;
                var indexOfLetter = find(alphabet, letter)!;

                if(modValue > indexOfLetter){

                    modValue = modValue-indexOfLetter;
                    newArray.append(alphabet[alphabet.count-modValue]);
                }else{
                    newArray.append(alphabet[indexOfLetter-modValue]);
                }

            }else{
                newArray.append(" ");
            }

        }

        return String(newArray);

    }
}

let startDate2 = NSDate();

let op = OPCaesarCipher();
var content2 = "Black malt berliner weisse, filter. Ibu degrees plato alcohol. ipa hard cider ester infusion conditioning tank. Dry stout bottom fermenting yeast wort chiller wort chiller lager hand pump ! All-malt dunkle bright beer grainy, original gravity wheat beer glass."

content2 = content2.lowercaseString
let arr2 = op.cipher(content2, shift: 13)
let endDate2 = NSDate();

let time2 = NSTimeInterval(endDate2.timeIntervalSinceDate(startDate2));

println("ciphered text: \n \(arr2) \n");
println("execution time: \n \(time2) \n");

I kept the same code example as yours, except I've used the same objects as you do in Objective-C: NSMutableString, etc.

Your example: 1.24008899927139

New version: 0.687016010284424

So I guess the library we're using to manipulate objects is as much important as the language, in our case.

Still, technically, yes, it appears that an "all Swift" solution is roughly 2x slower than a Swift + Cocoa solution (which should be roughly the same than Objective-C + Cocoa when compiled with equivalent options, IIRC).

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • That's interesting. So it turns out that using Swift with Cocoa is still better than using Swift built in library? The reason for this is because I'm working on something that serves as a comparison between the two. The main reason for using "all Swift" is because most articles/documentations/etc. I have read, claim, that Swift's functions and classes should be used whenever possible, because they are optimized best for Swift performance. I guess in this situation, it might not be 100% true? – ab1428x Apr 04 '15 at 11:49
  • 1
    I've encountered both situations (Swift std lib faster, Cocoa/Foundation faster), and I've no explanation other than "Swift is still in active development". ;) I believe *in the end* Swift will be (better||faster) but *for now*, most of the times the Foundation and Cocoa libs are the best, wether they're being called from Objective-C or Swift. Years of experience and iterations, I guess; compared with Swift wich is brand new. – Eric Aya Apr 04 '15 at 11:52
  • 1
    I just remembered an example: immutable arrays are faster in Swift std lib, whereas mutable arrays are faster in Cocoa. Etc. – Eric Aya Apr 04 '15 at 11:54
  • 1
    Thank you this helped me a lot and you're input will be very useful to what I'm working on. I was also leaning towards the fact that Swift is still in development, I'm glad someone else agreed, especially since I have written one program where Swift execution is around 9 seconds with optimization and Objective-C is... 43 minutes :) btw is there any available material online about how Swift optimization works? – ab1428x Apr 04 '15 at 11:55