簡體   English   中英

在AppKit中測量文本寬度的性能

[英]Performance of measuring text width in AppKit

在AppKit中有沒有辦法快速測量大量NSString對象(比如一百萬)的寬度? 我嘗試了3種不同的方法:

  • [NSString sizeWithAttributes:]
  • [NSAttributedString size]
  • NSLayoutManager (獲取文本寬度而不是高度)

    以下是一些性能指標

      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顯然是要走的路,但問題在於

  • 由於創建了重量級的NSTextStorage對象,因此內存占用空間 很大 (根據分析器而超過1GB)。
  • 創作時間長 所有花費的時間都是在創建上述字符串的過程中,這本身就是一個交易破壞者。(隨后測量NSTextStorage對象,其中創建和布置的字形只需要大約0.0002秒)。
  • 對於我想要做的事情,7秒仍然太慢 有更快的方法嗎? 在大約一秒鍾內測量一百萬個字符串?

    如果你想玩, 是github項目。

  • 這是我沒有嘗試過的一些想法。

    1. 直接使用Core Text 其他API建立在它之上。

    2. 並行。 所有現代Mac(甚至所有現代iOS設備)都有多個內核。 將字符串數組分成幾個子數組。 對於每個子數組,將塊提交到全局GCD隊列 在塊中,創建必要的Core Text或NSLayoutManager對象並測量子NSLayoutManager的字符串。 這兩種API都可以通過這種方式安全使用。 (核心文本) NSLayoutManager

    3. 關於“高內存占用”: 使用本地自動釋放池塊來減少峰值內存占用。

    4. 關於“所有花費的時間都是在創建上述字符串期間,這本身就是一個交易破壞者”:你是說所有的時間花在這些方面:

       double random = (double)arc4random_uniform(1000) / 1000; NSString *randomNumber = [NSString stringWithFormat:@"%f", random]; 

      格式化浮點數很昂貴。 這是你的真實用例嗎? 如果你只想格式化n / 1000形式的隨機有理數0≤n<1000,那么有更快的方法。 此外,在許多字體中,所有數字都具有相同的寬度,因此很容易排版數字列。 如果選擇這樣的字體,則可以避免首先測量字符串。

    UPDATE

    這是我用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.

     
    粵ICP備18138465號  © 2020-2024 STACKOOM.COM