2014-10-19 65 views
0

比較目標C與Swift中使用NSXMLParser的性能,存在較大的性能差異。目標C中僅註冊didStartElement,didEndElementfoundCharacters的性能爲〜17 MB/s,但Swift中的低至〜1.4 MB/s(當轉換爲String時,請參見下文)。代碼以發佈(優化)模式運行。使用NSXMLParser的迅速性能

目標C:

#import <Foundation/Foundation.h> 

@interface MyDelegate: NSObject <NSXMLParserDelegate> { 
    @public 
    int didStartElement; 
    int didEndElement; 
    int foundCharacters; 
} 
@end 

@implementation MyDelegate 
-(MyDelegate *)init { 
    didStartElement = 0; 
    didEndElement = 0; 
    foundCharacters = 0; 
    return self; 
} 

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { 
    didStartElement += 1; 
} 

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { 
    didEndElement += 1; 
} 

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 
    foundCharacters += 1; 
} 
@end 

int main(int argc, const char * argv[]) { 
    @autoreleasepool { 
     NSURL *input = [NSURL fileURLWithPath: [[NSProcessInfo processInfo] arguments][1]]; 

     NSError *error; 

     if (![input checkResourceIsReachableAndReturnError:&error]) { 
      NSLog(error.description); 
      abort(); 
     } 

     NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:input]; 
     MyDelegate *delegate = [[MyDelegate alloc] init]; 
     parser.delegate = delegate; 

     NSDate *start = [NSDate new]; 
     if (![parser parse]) { 
      NSLog(parser.parserError.description); 
     } 
     NSDate *end = [NSDate new]; 

     NSLog(@"Done. #didStartElement: %d, #didEndElement: %d, #foundCharacters: %d", delegate->didStartElement, delegate->didEndElement, delegate->foundCharacters); 

     NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:input.path error:&error]; 

     // Determine MB/s 
     if (error != nil) { 
      NSLog(@"%@", error); 
      abort(); 
     } 

     double throughput = ((NSNumber *)[attrs valueForKey:NSFileSize]).doubleValue/[end timeIntervalSinceDate:start]/1e6; 
     NSLog(@"Throughput %f MB/s", throughput); 
    } 
    return 0; 
} 

斯威夫特:

import Foundation 

var input = NSURL(fileURLWithPath: Process.arguments[1])! 
var error: NSError? 
if !input.checkResourceIsReachableAndReturnError(&error) { 
    println(error) 
    abort() 
} 

class MyDelegate: NSObject, NSXMLParserDelegate { 
    var didStartElement = 0 
    var didEndElement = 0 
    var foundCharacters = 0 

    func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [NSObject : AnyObject]) { 
     didStartElement += 1 
    } 

    func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { 
     didEndElement += 1 
    } 

    func parser(parser: NSXMLParser, foundCharacters string: String) { 
     foundCharacters += 1 
    } 
} 


var parser = NSXMLParser(contentsOfURL: input)! 
println(input) 
var delegate = MyDelegate() 
parser.delegate = delegate 

var start = NSDate() 
parser.parse() 
var end = NSDate() 

println("Done. #didStartElement: \(delegate.didStartElement), #didEndElement \(delegate.didEndElement), #foundCharacters \(delegate.foundCharacters)") 

// Determine MB/s 
var attrs = NSFileManager.defaultManager().attributesOfItemAtPath(input.path!, error: &error) 
if error != nil { 
    println(error!) 
    abort() 
} 
var throughput = Double(attrs![NSFileSize]! as Int)/end.timeIntervalSinceDate(start)/1e6 
println("Throughput \(throughput) MB/s") 

鑄造時大量的性能損失;看到在didStartElement爲用於attributes參數的類型定義的區別:

NSDictionary:19 MB/s的
[NSObject: AnyObject]:8.5百萬字節/秒
[String: String]:1.4百萬字節/秒

使用計數器儀表,顯然36%的時間花費在將字典轉換爲Swift上(使用[NSObject: AnyObject]): Swift NSXMLParser Counter Instrument Results

由於節點的屬性與進一步處理相關,因此c將它們轉移到Swift字符串是無法避免的。如何仍然在Swift中獲得不錯的處理性能?

更新

當用C直接使用的libxml2的SAX解析器,性能大約是110 MB /秒。所以這裏確實存在一些性能問題。

回答

1

我建議使用SAX風格的解析器和C API作爲基礎的XML解析器(例如libxml2)。創建NSDictionary s和特別是NSString s是過於昂貴(恕我直言,不必要)。因此,當從XML解析器中獲得的數據結構直接創建Swift容器和Swift字符串時,我們可能至少會保證這些成本。

但是,我不知道創建Swift字符串和Swift字典的代價是多麼昂貴。 Swift和它的圖書館仍處於起步階段。也

編輯

How to use the CoreAudio API in Swift

Function callback from C to Swift

+0

使用libxml的SAX解析器需要定義回調。目前無法從Swift函數創建C函數指針。那麼這甚至可能繞過Objective C和橋接? – bouke 2014-10-19 15:33:36

+0

我擔心,傳遞一個指向Swift函數的指針在C代碼中被調用尚不支持。這個問題需要調查。有一個有趣的觀察:http://stackoverflow.com/questions/24107099/function-callback-from-c-to-swift。 – CouchDeveloper 2014-10-19 15:43:53

+0

謝謝!雖然目前不可能直接從Swift實現libxml的sax解析器,但可以使用它的ObjC包裝器。通過將字符指針傳遞給Swift,從ObjC到Swift沒有太多橋接開銷。 – bouke 2014-11-01 09:44:38

1

現在可以通過將字典參數聲明爲NSDictionary而不是Swift字典來解決現在的轉換開銷。

+0

這隻會推遲到其他地方,其中字典值正在使用的地方。他們需要被轉換爲字符串的地方。 – bouke 2014-10-19 16:07:41

+0

它會避免字典轉換,而不是字符串轉換。 – 2014-10-19 16:47:23