为什么Python中的程序比Objective-C更快?

daw*_*awg 18 python objective-c nsstring

我对Python 中这个用于循环遍历大词列表的算法的小例子感兴趣.我正在写一些"工具",这将允许我以与Python类似的方式切片Objective-C字符串或数组.

具体来说,这个优雅的解决方案引起了我的注意,非常快速地执行,它使用字符串切片作为算法的关键元素.尝试解决这个没有切片!

我使用下面的Moby单词列表复制了我的本地版本./usr/share/dict/words如果您不想下载Moby,可以使用.源只是一个类似于字典的大型独特单词列表.

#!/usr/bin/env python

count=0
words = set(line.strip() for line in   
           open("/Users/andrew/Downloads/Moby/mwords/354984si.ngl"))
for w in words:
    even, odd = w[::2], w[1::2]
    if even in words and odd in words:
        count+=1

print count      
Run Code Online (Sandbox Code Playgroud)

这个脚本将a)由Python解释; b)读取4.1 MB,354,983字的Moby字典文件; c)剥去线条; d)将线条放入一组,并且; e)并找到所有组合,其中平均值和给定单词的几率也是单词.这在MacBook Pro上执行约0.73秒.

我试图在Objective-C中重写相同的程序.我是这种语言的初学者,所以请放轻松,但请指出错误.

#import <Foundation/Foundation.h>

NSString *sliceString(NSString *inString, NSUInteger start, NSUInteger stop, 
        NSUInteger step){
    NSUInteger strLength = [inString length];

    if(stop > strLength) {
        stop = strLength;
    }

    if(start > strLength) {
        start = strLength;
    }

    NSUInteger capacity = (stop-start)/step;
    NSMutableString *rtr=[NSMutableString stringWithCapacity:capacity];    

    for(NSUInteger i=start; i < stop; i+=step){
        [rtr appendFormat:@"%c",[inString characterAtIndex:i]];
    }
    return rtr;
}

NSSet * getDictWords(NSString *path){

    NSError *error = nil;
    NSString *words = [[NSString alloc] initWithContentsOfFile:path
                         encoding:NSUTF8StringEncoding error:&error];
    NSCharacterSet *sep=[NSCharacterSet newlineCharacterSet];
    NSPredicate *noEmptyStrings = 
                     [NSPredicate predicateWithFormat:@"SELF != ''"];

    if (words == nil) {
        // deal with error ...
    }
    // ...

    NSArray *temp=[words componentsSeparatedByCharactersInSet:sep];
    NSArray *lines = 
        [temp filteredArrayUsingPredicate:noEmptyStrings];

    NSSet *rtr=[NSSet setWithArray:lines];

    NSLog(@"lines: %lul, word set: %lul",[lines count],[rtr count]);
    [words release];

    return rtr;    
}

int main (int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    int count=0;

    NSSet *dict = 
       getDictWords(@"/Users/andrew/Downloads/Moby/mwords/354984si.ngl");

    NSLog(@"Start");

    for(NSString *element in dict){
        NSString *odd_char=sliceString(element, 1,[element length], 2);
        NSString *even_char=sliceString(element, 0, [element length], 2);
        if([dict member:even_char] && [dict member:odd_char]){
            count++;
        }

    }    
    NSLog(@"count=%i",count);

    [pool drain];
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Objective-C版本产生相同的结果(13,341个单词),但需要将近3秒钟才能完成.我必须做一些非常错误的错误,因为编译语言比脚本语言慢了3倍,但如果我能理解为什么,我会感到很沮丧.

基本算法是相同的:读取线条,剥离它们,并将它们放在一个集合中.

我对慢速的猜测是对NSString元素的处理,但我不知道另一种选择.

编辑

我编辑Python是这样的:

#!/usr/bin/env python
import codecs
count=0
words = set(line.strip() for line in 
     codecs.open("/Users/andrew/Downloads/Moby/mwords/354984si.ngl",
          encoding='utf-8'))
for w in words:
    if w[::2] in words and w[1::2] in words:
        count+=1

print count 
Run Code Online (Sandbox Code Playgroud)

因为utf-8与utf-8 NSString在同一平面上.这使Python的速度降低到1.9秒.

我还将切片测试切换为短路类型,如Python和obj-c版本所建议的那样.现在他们接近相同的速度.我也尝试使用C数组而不是NSStrings,这要快得多,但并不容易.你也可以放弃utf-8支持.

Python真的很酷......

编辑2

我找到了一个瓶颈,大大加快了速度.我没有使用该[rtr appendFormat:@"%c",[inString characterAtIndex:i]];方法将字符附加到返回字符串,而是使用了:

for(NSUInteger i=start; i < stop; i+=step){
    buf[0]=[inString characterAtIndex:i];
    [rtr appendString:[NSString stringWithCharacters:buf length:1]];
}
Run Code Online (Sandbox Code Playgroud)

现在我终于可以声称Objective-C版本比Python版本更快 - 但不是很多.

nco*_*lan 9

请记住,在Python版本已经被写入时对CPython的执行(尤其是文件输入缓冲,字符串的切片和哈希表查找,以检查是否有很多繁重向下移动到高度优化的C代码even,并oddwords).

也就是说,您似乎在Objective-C代码中将文件解码为UTF-8,但在Python代码中将文件保留为二进制文件.在Objective-C版本中使用Unicode NSString,但是Python版本中的8位字节字符串实际上并不是一个公平的比较 - 如果您曾经使用codecs.open()声明的文件打开文件,我会期望Python版本的性能显着下降编码"utf-8".

您还要进行完整的第二遍以去除Objective-C中的空行,而Python代码中不存在此类步骤.