[英]Performance of measuring text width in AppKit
在AppKit中有沒有辦法快速測量大量NSString對象(比如一百萬)的寬度? 我嘗試了3種不同的方法:
Count \\ Mechanism sizeWithAttributes NSAttributedString NSLayoutManager
1000 0.057 0.031 0.007
10000 0.329 0.325 0.064
100000 3.06 3.14 0.689
1000000 29.5 31.3 7.06
NSLayoutManager顯然是要走的路,但問題在於
如果你想玩, 這是github項目。
這是我沒有嘗試過的一些想法。
直接使用Core Text 。 其他API建立在它之上。
並行。 所有現代Mac(甚至所有現代iOS設備)都有多個內核。 將字符串數組分成幾個子數組。 對於每個子數組,將塊提交到全局GCD隊列 。 在塊中,創建必要的Core Text或NSLayoutManager
對象並測量子NSLayoutManager
的字符串。 這兩種API都可以通過這種方式安全使用。 (核心文本) ( NSLayoutManager
)
關於“高內存占用”: 使用本地自動釋放池塊來減少峰值內存占用。
關於“所有花費的時間都是在創建上述字符串期間,這本身就是一個交易破壞者”:你是說所有的時間花在這些方面:
double random = (double)arc4random_uniform(1000) / 1000; NSString *randomNumber = [NSString stringWithFormat:@"%f", random];
格式化浮點數很昂貴。 這是你的真實用例嗎? 如果你只想格式化n / 1000形式的隨機有理數0≤n<1000,那么有更快的方法。 此外,在許多字體中,所有數字都具有相同的寬度,因此很容易排版數字列。 如果選擇這樣的字體,則可以避免首先測量字符串。
這是我用Core Text提出的最快的代碼。 發送的版本幾乎是我的Core i7 MacBook Pro上單線程版本的兩倍。 我的項目分支就在這里 。
static CGFloat maxWidthOfStringsUsingCTFramesetter(
NSArray *strings, NSRange range) {
NSString *bigString =
[[strings subarrayWithRange:range] componentsJoinedByString:@"\n"];
NSAttributedString *richText =
[[NSAttributedString alloc]
initWithString:bigString
attributes:@{ NSFontAttributeName: (__bridge NSFont *)font }];
CGPathRef path =
CGPathCreateWithRect(CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX), NULL);
CGFloat width = 0.0;
CTFramesetterRef setter =
CTFramesetterCreateWithAttributedString(
(__bridge CFAttributedStringRef)richText);
CTFrameRef frame =
CTFramesetterCreateFrame(
setter, CFRangeMake(0, bigString.length), path, NULL);
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
for (id item in lines) {
CTLineRef line = (__bridge CTLineRef)item;
width = MAX(width, CTLineGetTypographicBounds(line, NULL, NULL, NULL));
}
CFRelease(frame);
CFRelease(setter);
CFRelease(path);
return (CGFloat)width;
}
static void test_CTFramesetter() {
runTest(__func__, ^{
return maxWidthOfStringsUsingCTFramesetter(
testStrings, NSMakeRange(0, testStrings.count));
});
}
static void test_CTFramesetter_dispatched() {
runTest(__func__, ^{
dispatch_queue_t gatherQueue = dispatch_queue_create(
"test_CTFramesetter_dispatched result-gathering queue", nil);
dispatch_queue_t runQueue =
dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
dispatch_group_t group = dispatch_group_create();
__block CGFloat gatheredWidth = 0.0;
const size_t Parallelism = 16;
const size_t totalCount = testStrings.count;
// Force unsigned long to get 64-bit math to avoid overflow for
// large totalCounts.
for (unsigned long i = 0; i < Parallelism; ++i) {
NSUInteger start = (totalCount * i) / Parallelism;
NSUInteger end = (totalCount * (i + 1)) / Parallelism;
NSRange range = NSMakeRange(start, end - start);
dispatch_group_async(group, runQueue, ^{
double width =
maxWidthOfStringsUsingCTFramesetter(testStrings, range);
dispatch_sync(gatherQueue, ^{
gatheredWidth = MAX(gatheredWidth, width);
});
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return gatheredWidth;
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.