使用 NSXMLParser 的 Swift 性能 [英] Swift performance using NSXMLParser

查看:68
本文介绍了使用 NSXMLParser 的 Swift 性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比较在目标 C 和 Swift 中使用 NSXMLParser 的性能,存在很大的性能差异.仅注册 didStartElementdidEndElementfoundCharacters 在目标 C 中的性能约为 17 MB/s,但在目标 C 中的性能约为 1.4 MB/sSwift(当转换为字符串时,见下文).代码在发布(优化)模式下运行.

Comparing the performance of using NSXMLParser within objective C vs Swift, there's a large performance discrepancy. Only registering didStartElement, didEndElement and foundCharacters has a performance of ~17 MB/s in objective C, but a low ~1.4 MB/s in Swift (when casting to String, see below). The code is run in Release (optimized) mode.

目标 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")

投射时损失了很多性能;查看 didStartElementattributes 参数的类型定义的区别:

A lot of performance is lost when casting; see the difference for the type definitions for the attributes argument in didStartElement:

NSDictionary:19 MB/秒
[NSObject:AnyObject]:8.5 MB/s
[String: String]: 1.4 MB/s

NSDictionary: 19 MB/s
[NSObject: AnyObject]: 8.5 MB/s
[String: String]: 1.4 MB/s

使用 Counter Instrument,显然 36% 的时间花在将字典转换为 Swift(使用 [NSObject: AnyObject]):

Using the Counter Instrument, apparently 36% of the time is being spent on converting the dictionary to Swift (using [NSObject: AnyObject]):

由于节点的属性与进一步处理相关,因此无法避免将它们转换为 Swift String.如何在 Swift 中仍然获得不错的处理性能?

As the attributes of the nodes are relevant for further processing, casting them to Swift String can't be avoided. How to still get a decent processing performance in Swift?

更新

直接在 C 中使用 libxml2 的 sax 解析器时,性能约为 110 MB/s.所以这里确实存在一些性能问题.

When using libxml2's sax parser directly in C, the performance is around 110 MB/s. So there really is some performance issue here.

推荐答案

我建议使用带有 C API 的 SAX 样式解析器作为底层 XML 解析器(例如 libxml2).创建 NSDictionary特别是 NSString 非常昂贵(恕我直言也是不必要的).因此,当从 XML 解析器获得的数据结构中直接创建 Swift 容器和 Swift 字符串时,我们至少可以节省这些成本.

I would suggest to use a SAX style parser with a C API as the underlying XML parser (libxml2 for instance). Creating NSDictionarys and especially NSStrings is unduly expensive (unnecessary as well IMHO). So, we might safe at least these costs when creating directly Swift containers and Swift Strings from the data structures obtained from the XML parser.

然而,我不知道创建 Swift Strings 和 Swift Dictionaries 的成本有多高.Swift 及其库仍处于起步阶段.

I do not know, however, how expensive it is to create Swift Strings and Swift Dictionaries. Swift and its library is still in its infancy.

编辑

另请参阅如何在 Swift 中使用 CoreAudio API,

从 C 到 Swift 的函数回调.

这篇关于使用 NSXMLParser 的 Swift 性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆