Skip to content

搞英语 → 看世界

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

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

高效地将长字符串拆分成行

Posted on 2025-09-08

假设你有一个长字符串,并且希望每 72 个字符插入一个换行符。如果你需要将公钥写入文本文件,则可能需要这样做。

一个简单的 C 函数就足够了。我用字母 K 来表示每行的长度。我将数据从输入缓冲区复制到输出缓冲区。

 void insert_line_feed ( const char *缓冲区, size_t长度, 
  
int K , char *输出) { 
  
如果( K = = 0 ) { 
  
memcpy (输出,缓冲区,长度) ; 
  
返回; 
  
} 
  
size_t输入位置= 0 ; 
  
size_t下一行进给= K ; 
  
while (输入位置<长度) { 
  
输出[ 0 ] =缓冲区[输入位置] ; 
  
输出++ ; 
  
输入位置++ ; 
  
下一行进纸-- ; 
  
如果( next_line_feed == 0 ) { 
  
输出[ 0 ] = '\n' ; 
  
输出++ ; 
  
下一行进纸= K ; 
  
} 
  
} 
  
} 
  

这种逐个字符复制的过程可能效率不高。为了加快速度,我们可以调用memcpy来复制数据块。

 void insert_line_feed_memcpy ( const char *缓冲区, size_t长度, int K , 
  
char *输出) { 
  
如果( K = = 0 ) { 
  
memcpy (输出,缓冲区,长度) ; 
  
返回; 
  
} 
  
size_t输入位置= 0 ; 
  
while (输入位置+ K <长度) { 
  
std :: memcpy (输出,缓冲区+输入位置, K ) ; 
  
输出+ = K ; 
  
输入位置+ = K ; 
  
输出[ 0 ] = '\n' ; 
  
输出++ ; 
  
} 
  
std :: memcpy (输出,缓冲区+输入位置,长度-输入位置) ; 
  
}

memcpy函数很可能被简化成几条指令。例如,如果你针对最新的 AMD 处理器 (Zen 5) 进行编译,当行数 (K) 为 64 时,它可能只会生成两条指令(两条vmovups 指令)。

我们可以做得更好吗?

总的来说,我认为你不会比使用memcpy函数做得更好。编译器只是对其进行了很好的优化。

然而,探索是否应该刻意使用 SIMD 指令来优化这段代码或许会很有趣。SIMD(单指令多数据)指令可以用一条指令同时处理多个数据元素: memcpy函数会自动使用它。我们可以通过内部函数来利用 SIMD 指令,这些内部函数是编译器提供的接口,可以直接访问特定于处理器的指令,从而优化性能,同时保持高级代码的可读性。

让我重点介绍 AVX2,这是所有 x64(Intel 和 AMD)处理器实际上都支持的指令集。我们可以加载 32 字节寄存器并写入 32 字节寄存器。因此,我们需要一个函数,该函数接受一个 32 字节寄存器并在其中的某个位置( N )插入换行符。对于N小于 16 的情况,该函数会使用_mm256_alignr_epi8和_mm256_blend_epi32将输入向量右移一个字节以正确对齐数据,然后再应用重排掩码并插入换行符。当N等于或大于 16 时,它会直接使用预先计算的shuffle_masks数组中的重排掩码来重新排序输入字节并插入换行符,利用与“0x80”的比较来识别插入点,并将结果与​​换行符向量混合,以实现高效的并行处理。

内联__m256i insert_line_feed32 ( __m256i 输入, int N ) { 
  
__m256i 行进_向量= _mm256_set1_epi8 ( '\n' ) ; 
  
__m128i 身份= 
  
 _mm_setr_epi8 ( 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 
  
9、10、11、12、13、14、15 ) ;​​​​​​​​​​​​ 
  
如果( K > = 16 ) { 
  
 __m128i maskhi = _mm_loadu_si128 ( shuffle_masks [ N - 16 ] ) ; 
  
__m256i 掩码= _mm256_set_m128i ( maskhi ,身份) ; 
  
__m256i lf_pos = _mm256_cmpeq_epi8 (掩码, 
  
_mm256_set1_epi8 ( 0x80 ) ) ; 
  
__m256i shuffled = _mm256_shuffle_epi8 (输入,掩码) ; 
  
__m256i 结果= _mm256_blendv_epi8 (打乱, 
  
行进给向量, lf_pos ) ; 
  
返回结果; 
  
} 
  
// 将输入右移 1 个字节 
  
__m256i 移位= _mm256_alignr_epi8 ( 
  
输入, _mm256_permute2x128_si256 (输入,输入, 0x21 ) , 
  
15 ) ; 
  
输入= _mm256_blend_epi32 (输入,移位, 0xF0 ) ; 
  
 __m128i masklo = _mm_loadu_si128 ( shuffle_masks [ N ] ) ; 
  
 __m256i 掩码= _mm256_set_m128i (身份, masklo ) ; 
  
__m256i lf_pos = _mm256_cmpeq_epi8 (掩码, 
  
_mm256_set1_epi8 ( 0x80 ) ) ; 
  
__m256i shuffled = _mm256_shuffle_epi8 (输入,掩码) ; 
  
__m256i 结果= _mm256_blendv_epi8 (打乱, 
  
行进给向量, lf_pos ) ; 
  
返回结果; 
  
} 
  

使用这样一个奇特的函数能提高速度吗?让我们测试一下。我编写了一个基准测试。我在装有 GCC 12 的 Intel Ice Lake 处理器上输入了一个较大的字符串。

逐个字符 1.0 GB/秒 8.0 英寸/字节
内存复制 11 GB/秒 0.46 英寸/字节
AVX2 16 GB/秒 0.52 英寸/字节

在我的测试中,尽管使用了更多指令,但手工编写的 AVX2 方法比memcpy方法速度更快。然而,手工编写的 AVX2 方法使用更少的指令将数据存储到内存中。

原文: https://lemire.me/blog/2025/09/07/splitting-a-long-string-in-lines-efficiently/

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