Skip to content

搞英语 → 看世界

翻译英文优质信息和名人推特

Menu
  • 首页
  • 作者列表
  • 独立博客
  • 专业媒体
  • 名人推特
  • 邮件列表
  • 关于本站
Menu

快速计算 Latin 1 字符串的 UTF-8 大小(ARM NEON 版)

Posted on 2023-05-16

肖像2018facebook.jpg

虽然我们的大多数软件都依赖于 Unicode 字符串,但我们仍然经常遇到遗留编码,例如 Latin 1。在将 Latin 1 字符串转换为 Unicode(例如 UTF-8)之前,我们必须计算 UTF-8 字符串的大小。这相当简单:所有 ASCII 字符将 1 个字节映射到 1 个字节,而其他字符(代码点值从 128 到 256)将 1 个拉丁字节映射到 2 个 Unicode 字节(在 UTF-8 中)。

计算机使用字节表示字符串。大多数情况下,我们使用 Unicode 标准以字节表示字符。在线交换字符串的通用格式称为 UTF-8。它可以表示超过一百万个字符,同时保持与古老的 ASCII 格式的兼容性。

尽管我们的大部分软件堆栈已转移到 Unicode 字符串,但仍有较旧的标准,如用于欧洲字符串的 Latin 1。因此我们经常需要将 Latin 1 字符串转换为 UTF-8。首先计算最终 UTF-8 字符串的大小(以字节为单位)很有用。您可以编写一个简单的 C 函数来计算 Latin 1 输入的 UTF-8 大小,如下所示:

 size_t scalar_utf8_length ( const char * c , size_t len ) {  
   
size_t答案= 0 ;  
   
对于( size_t i = 0 ; i < len ; i + + ) {  
   
如果( ( c [ i ] > > 7 ) ) {回答+ + ; }  
   
}  
   
返回答案+ len ;  
   
}  
   
  
   

在Computing the UTF-8 size of a Latin 1 string quickly (AVX edition)中,我回顾了在 x64 处理器上解决此问题的更快技术。

ARM 处理器(如最近的 MacBook)怎么样?

Keyhan Vakil 提出了一个很好的解决方案,它依赖于 ARM 处理器中“缩小指令”的可用性。基本上,您可以获取一个 16 字节的向量寄存器,并通过截断或舍入结果来(虚拟地)创建一个 8 字节的寄存器。方便的是,您还可以将位移与缩小结合起来。

将成对的连续 8 位字视为 16 位字。例如,如果 16 位以aaaaaaaabbbbbbbbb开头,则四位移位和窄位创建字节值aaaabbbb 。事实上,如果将一个 16 位字移动 4 位并只保留结果的最低有效 8 位,那么

  1. 第二个 8 位字的最高 4 位成为结果中的最低 4 位
  2. 并且第一个 8 位字的最低有效 4 位成为最高有效 4 位。

这很方便,因为当比较为真(全为 1)时,向量化比较函数通常会生成填充字节。 C 中的最终算法如下所示:

 uint64_t utf8_length_kvakil ( const uint8_t * data , uint32_t length ) {  
   
  uint64_t 结果= 0 ;  
   
常量int车道= sizeof ( uint8x16_t ) ;  
   
  uint8_t rem = length %车道;  
   
const uint8_t * simd_end = data + ( length / lanes ) * lanes ;  
   
const uint8x16_t threshold = vdupq_n_u8 ( 0x80 ) ;  
   
对于( ;数据< simd_end ;数据+ =车道) {  
   
// 加载 16 位  
   
    uint8x16_t 输入= vld1q_u8 (数据) ;  
   
// 与阈值 (0x80) 比较  
   
    uint8x16_t withhighbit = vcgeq_u8 (输入,阈值) ;  
   
// 移动和缩小  
   
    uint8x8_t highbits = vshrn_n_u16 ( vreinterpretq_u16_u8 ( withhighbit ) , 4 ) ;  
   
// 每个字节有 0、4 或 8 位  
   
    uint8x8_t bitsperbyte = vcnt_u8 ( highbits ) ;  
   
// 将字节垂直求和到 uint16_t  
   
   结果+ = vaddlv_u8 ( bitsperbyte ) ;  
   
}  
   
  结果/ = 4 ; // 我们多算了 4 倍  
   
// 标量尾  
   
对于( uint8_t j = 0 ; j < rem ; j + + ) {  
   
    结果+ = ( simd_end [ j ] > > 7 ) ;  
   
}  
   
返回结果+长度;  
   
}  
   
  
   

快吗?

在我的 Apple M2 上,它的速度是编译器在足够大的输入下从标量代码生成的速度的三倍。观察编译器已经依赖向量指令。

标量码 ~6 GB/秒
霓虹灯代码 ~20 GB/秒

我的源代码可用。您的结果会有所不同。

你能打败瓦基尔吗?您当然可以减少指令数,但是一旦达到 20 GB/s 的速度,就很难在不达到内存和缓存速度限制的情况下更快地运行。

原文: https://lemire.me/blog/2023/05/15/computing-the-utf-8-size-of-a-latin-1-string-quickly-arm-neon-edition/

本站文章系自动翻译,站长会周期检查,如果有不当内容,请点此留言,非常感谢。
  • Abhinav
  • Abigail Pain
  • Adam Fortuna
  • Alberto Gallego
  • Alex Wlchan
  • Answer.AI
  • Arne Bahlo
  • Ben Carlson
  • Ben Kuhn
  • Bert Hubert
  • Bits about Money
  • Brian Krebs
  • ByteByteGo
  • Chip Huyen
  • Chips and Cheese
  • Christopher Butler
  • Colin Percival
  • Cool Infographics
  • Dan Sinker
  • David Walsh
  • Dmitry Dolzhenko
  • Dustin Curtis
  • Elad Gil
  • Ellie Huxtable
  • Ethan Marcotte
  • Exponential View
  • FAIL Blog
  • Founder Weekly
  • Geoffrey Huntley
  • Geoffrey Litt
  • Greg Mankiw
  • Henrique Dias
  • Hypercritical
  • IEEE Spectrum
  • Investment Talk
  • Jaz
  • Jeff Geerling
  • Jonas Hietala
  • Josh Comeau
  • Lenny Rachitsky
  • Liz Danzico
  • Lou Plummer
  • Luke Wroblewski
  • Matt Baer
  • Matt Stoller
  • Matthias Endler
  • Mert Bulan
  • Mostly metrics
  • News Letter
  • NextDraft
  • Non_Interactive
  • Not Boring
  • One Useful Thing
  • Phil Eaton
  • Product Market Fit
  • Readwise
  • ReedyBear
  • Robert Heaton
  • Ruben Schade
  • Sage Economics
  • Sam Altman
  • Sam Rose
  • selfh.st
  • Shtetl-Optimized
  • Simon schreibt
  • Slashdot
  • Small Good Things
  • Taylor Troesh
  • Telegram Blog
  • The Macro Compass
  • The Pomp Letter
  • thesephist
  • Thinking Deep & Wide
  • Tim Kellogg
  • Understanding AI
  • 英文媒体
  • 英文推特
  • 英文独立博客
©2025 搞英语 → 看世界 | Design: Newspaperly WordPress Theme