【问题标题】:Swift: High performance String transformation?Swift:高性能字符串转换?
【发布时间】:2018-10-03 05:44:16
【问题描述】:

我有大约 36000 个文件,每个文件包含大约 250 个单词,我需要通过删除每个小数字符并将所有大写字符小写来转换每个单词。

我将我读到的每个单词传递给一个函数,该函数返回修改后的单词 让我稍后索引它。

问题在于,字符数量如此之多,速度非常慢。

我一直在尝试迭代我的字符串,像在 C 中一样操作 Characters,使用 ascii 值,但是 Swift 对此有点糟糕,需要一直从 Characters 重构字符串。

我带来了这段小代码,但我在 Swift 中对这个精确主题的了解是广泛的,以便找到更好的。另外,我使用的是 Swift 4.2。

func processToken(token: String) -> String {
    var result = String()

    for ascii in token.utf8 {
        if ascii > 64 && ascii < 91 {
            result.append(String(UnicodeScalar(UInt8(ascii + 32))))
        }
        else if ascii > 96 && ascii < 123 {
            result.append(String(UnicodeScalar(UInt8(ascii))))
        }
    }
    return result
}

【问题讨论】:

  • 如果你想要最好的性能,你可以用 C/Objective-C 编写关键部分。
  • @cristik 这实际上是一个非常糟糕的建议。 Swift 并不比 C 慢。它与语言无关,它与算法有关。
  • @Sulthan 有 36000 个文件,这意味着包含 char* 的样板 Swift 代码有 36k 次。请不要试图说服我这并不比 C 慢。
  • Swift String 不擅长处理 UTF-8 字节序列,因为它的内部表示基于 UTF-16。所以,如果你把.utf8改成.utf16(有些部分需要根据变化进行修改),性能会有所提高。如果你想要一个关键的性能,你最好将文件读取为Data 并通过指针对其进行操作。 Swift 指针和 C 一样快。
  • 更多信息会有所帮助:您认为“十进制字符”是什么——只有 0..9,还是来自非拉丁字母的十进制字符?为什么将文本分成单词(以及如何)?您的代码只考虑 a-z 和 A-Z,其他语言/字母的字母呢?剩下的字符(空格、标点符号、表情符号……)应该怎么做?

标签: swift string nsstring


【解决方案1】:

我写了一个应该很快的解决方案。这是一个 Objective-C 函数,主要使用普通的旧 C。为什么是 C?因为我们确切地知道 C 运行时的作用,它基本上提供了一个超薄的内存包装器。另一方面,Swift 运行时的许多低级细节是未知的,并且有充分的理由。但是,可以安全地假设 Swift 具有比普通 C 更高的性能开销。在这方面击败 C 的唯一方法是一直使用汇编程序或打开您选择的十六进制编辑器并输入操作码和操作数自己。不要误解我的意思,出于很多原因,应该始终首选 Swift,但如果性能至关重要,那么 Objective-C / C 是首选工具。

所以这里出现了代码 sn-p。它需要一个 NSString 并去掉所有数字并将所有大写字符转换为对应的小写字符。

-  ( NSString* ) convert:( NSString* ) source
{
    const char bitmaskNonAsciiChar =  ( char ) ( 1 << 7 );
    const char* cSource = [ source UTF8String ];
    size_t strLength = strlen( cSource );
    size_t bufferSize = sizeof( char ) * strLength + 1;

    char *result = ( char* ) malloc( bufferSize );
    memset( result, '\0', bufferSize );

    int currentIndex = 0;
    for( int i = 0; i < strLength; ++i )
    {
        // Check if this is an UTF-8 character that's longer than one byte. If so, it can't be an ASCII character and we can just write this character to the result buffer.
        if( ( cSource[ i ] & bitmaskNonAsciiChar ) == bitmaskNonAsciiChar )
        {
            result[ currentIndex++ ] = cSource[ i ];
            continue;
        }

        // Now we know it is an ASCII character, so we need to check for digits and upper-case characters.
        if( cSource[ i ] >= ( char ) 46 && cSource[ i ] <= ( char ) 57 )
        {
            continue;
        }

        if( cSource[ i ] >= ( char ) 65 && cSource[ i ] <= ( char ) 90 )
        {
            result[ currentIndex++ ] = cSource[ i ] + ( char ) 32;
            continue;
        }

        // It's an ASCII character that is neither a digit nor upper-cased, so we just write it to the result buffer.
        result[ currentIndex++ ] = cSource[ i ];
    }

    NSString *resultString = [ NSString stringWithUTF8String: result ];
    free( result );

    return resultString;
}

这是一个稍微修改过的版本,如果您确定输入大小相当小,可以使用它。此版本不使用 malloc 在堆上创建结果缓冲区,而是简单地使用堆栈内存。如果您想逐字逐句地运行它,那么为每次调用分配所有这些内存是浪费大量时间。但是,如果您打算逐个文件地运行它并且您不知道文件可能会变得多大,那么第一个版本可能会更好。

-  ( NSString* ) convert:( NSString* ) source
{
    const char bitmaskNonAsciiChar =  ( char ) ( 1 << 7 );
    const char* cSource = [ source UTF8String ];
    size_t strLength = strlen( cSource );
    size_t bufferSize = sizeof( char ) * strLength + 1;

    char result[ bufferSize ];
    memset( result, '\0', bufferSize );

    int currentIndex = 0;
    for( int i = 0; i < strLength; ++i )
    {
        // Check if this is an UTF-8 character that's longer than one byte. If so, it can't be an ASCII character and we can just write this character to the result buffer.
        if( ( cSource[ i ] & bitmaskNonAsciiChar ) == bitmaskNonAsciiChar )
        {
            result[ currentIndex++ ] = cSource[ i ];
            continue;
        }

        // Now we know it is an ASCII character, so we need to check for digits and upper-case characters.
        if( cSource[ i ] >= ( char ) 46 && cSource[ i ] <= ( char ) 57 )
        {
            continue;
        }

        if( cSource[ i ] >= ( char ) 65 && cSource[ i ] <= ( char ) 90 )
        {
            result[ currentIndex++ ] = cSource[ i ] + ( char ) 32;
            continue;
        }

        // It's an ASCII character that is neither a digit nor upper-cased, so we just write it to the result buffer.
        result[ currentIndex++ ] = cSource[ i ];
    }

    NSString *resultString = [ NSString stringWithUTF8String: result ];

    return resultString;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多