Сравнивая производительность использования NSXMLParser в объективных C и Swift, существует большое несоответствие производительности. Только регистрация didStartElement
, didEndElement
и foundCharacters
имеет производительность ~ 17 Мбайт/с в объективе C, но низкий ~ 1,4 МБ/с в Swift (при кастинге в String, см. Ниже). Код запускается в режиме Release (оптимизирован).Быстрая работа с использованием NSXMLParser
Objective 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;
}
Swift:
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")
Много производительности теряется при литье; увидеть разницу для определения типа для attributes
аргумента в didStartElement
:
NSDictionary
: 19 МБ/с
[NSObject: AnyObject]
: 8,5 Мб/с
[String: String]
: 1,4 Мб/с
Использование счетчика на инструменте, по-видимому, 36% времени тратится на преобразовании словаря в Swift (с использованием [NSObject: AnyObject]
):
в качестве атрибутов узлов имеют значение для дальнейшей обработки, с не следует избегать их для Swift String. Как все еще получить приличную производительность обработки в Swift?
Update
При использовании парсера SAX Libxml2 непосредственно в C, производительность составляет около 110 Мбайт/с. Так что здесь действительно проблема с производительностью.
Использование анализатора SAX от libxml требует определения обратных вызовов. В настоящее время невозможно создать C Function Pointer из функций Swift. Так возможно ли это, минуя Objective C и мостик? – bouke
Я боюсь, передача указателя на функцию Swift, которая вызывается в C-коде, пока не поддерживается. Эту проблему нужно исследовать. Есть интересное замечание: http://stackoverflow.com/questions/24107099/function-callback-from-c-to-swift. – CouchDeveloper
Спасибо!Хотя в настоящее время невозможно реализовать саксовый анализатор libxml непосредственно из Swift, для него можно использовать обертку ObjC. Передавая указатели на символы Swift, на ObjC до Swift не так много накладных расходов. – bouke