如何更快地在 byte[] 中搜索字节?

Posted

技术标签:

【中文标题】如何更快地在 byte[] 中搜索字节?【英文标题】:How I can search for byte in byte[] faster? 【发布时间】:2019-10-04 15:36:20 【问题描述】:

我在 InputStream 中进行简单的行号计算(计算 NewLines #10 的数量)

for (int i = 0; i < readBytes ; i++) 
    if ( b[ i + off ] == 10 )                      // New Line (10)
        rowCount++;
    

我可以做得更快吗?没有一个字节的迭代? 可能我正在寻找一些能够使用 CPU 特定指令(simd/sse)的类。

所有代码:

@Override
public int read(byte[] b, int off, int len) throws IOException 

    int readBytes = in.read(b, off, len);

    for (int i = 0; i < readBytes ; i++) 
        hadBytes = true;                                // at least once we read something
        lastByteIsNewLine = false;
        if ( b[ i + off ] == 10 )                      // New Line (10)
            rowCount++;
            lastByteIsNewLine = (i == readBytes - 1);   // last byte in buffer was the newline
        
    

    if ( hadBytes && readBytes == -1 && ! lastByteIsNewLine )    // file is not empty + EOF + last byte was not NewLine
        rowCount++;
    

    return readBytes;

【问题讨论】:

什么是readBytes?请发布其内容 readBytes == 字节大小[] 老实说,没有。没有更快的方法来做到这一点。它已经和汇编语言几乎一样了。 @VGR 你是说 JIT 能够产生一个 vectorized version ,像在汇编中一样一次比较 16+ 个字节? @thatotherguy:一个好的 JIT 很有可能可以矢量化这个循环。 【参考方案1】:

在我的系统上,只需将 lastByteIsNewLinehasBytes 部分移出循环即可带来约 10% 的改进*:

  public int read(byte[] b, int off, int len) throws IOException 

    int readBytes = in.read(b, off, len);

    for (int i = 0; i < readBytes ; i++) 
      if ( b[ i + off ] == 10 ) 
        rowCount++;
      
    
    hadBytes |= readBytes > 0;
    lastByteIsNewLine = (readBytes > 0 ? b[readBytes+off-1] == 10 : false);

    if ( hadBytes && readBytes == -1 && ! lastByteIsNewLine )  
      rowCount++;
    

    return readBytes;
  

* 6000ms vs 6700ms 1000 次迭代,从填充任意文本的 ByteArrayInputStream 读取的 10MB 缓冲区。

【讨论】:

一旦设置为truehadBytes 永远不应设置为false(在后续调用中)。使用hadBytes |= readBytes &gt; 0【参考方案2】:

我从that other guy's 改进开始,并将数组索引计算和字段访问提升到for 循环之外。

根据我的 JMH 基准,这节省了另一个 25%,“​​那个人的”实现时钟为 3.6 ms/op,此版本为 2.7 ms/op。 (这里,一个操作是读取大约 5000 行随机长度的 ~10 MB ByteArrayInputStream)。

public int read(byte[] buffer, int off, int len) throws IOException 
  int n = in.read(buffer, off, len);
  notEmpty |= n > 0;
  int count = notEmpty && n < 0 && !trailingLineFeed ? 1 : 0;
  trailingLineFeed = (n > 0) && buffer[n + off - 1] == '\n';
  for (int max = off + n, idx = off; idx < max;) 
    if (buffer[idx++] == '\n') ++count;
  
  rowCount += count;
  return n;

真正影响性能的事情:在数组上向后索引。

无关紧要的事情:将值与更易读的 '\n' 而不是 10 进行比较。

令人惊讶的是(无论如何对我来说),仅使用其中一种技巧本身似乎并没有提高性能。它们只是一起使用才有所作为。

【讨论】:

第二行(在 trailingLineFeed = 之前)实际上有点错误int count = ,因为最后一次调用 (n=-1) 破坏了 trailingLineFeed 并使其为假。谢谢。 @DenisZhuravlev 我想我修好了。【参考方案3】:

readBytes转换成String后就可以轻松搜索了:

String stringBytes = new String(readBytes);

要获取出现次数:

int rowCount = StringUtils.countMatches(stringBytes, "\n");

只知道\n是否包含在readBytes中:

boolean newLineFound = stringBytes.contains("\n");

【讨论】:

刚刚试过这个。它慢了 3 倍。 ``` String stringBytes = new String(b);诠释 i = -1; while ( i 仍然慢 3 倍 String stringBytes = new String(b); rowCount += StringUtils.countOccurrencesOf(stringBytes, "\n"); 我查看了 StringUtils.countOccurrencesOf。它使用 String.indexOf 逐字节执行相同的迭代。【参考方案4】:

好吧,与其尝试加快特定部分的速度(我认为您不能),不如尝试使用不同的方法。这是一个类,可用于在从 InputStream 读取时跟踪行数。

public class RowCounter 
    private static final int LF = 10;
    private int rowCount = 0;
    private int lastByte = 0;

    public int getRowCount() 
        return rowCount;
    

    public void addByte(int b) 
        if (lastByte == LF) 
            rowCount++;
        
        lastByte = b;
    

    public void addBytes(byte[] b, int offset, int length) 
        if (length <= 0) return;
        if (lastByte == LF) rowCount++;

        int lastIndex = offset + length - 1;
        for (int i = offset; i < lastIndex; i++) 
            if (b[i] == LF) rowCount++;
        
        lastByte = b[lastIndex];
    

那么在读取一个InputStream的时候,就可以这样使用了。

InputStream is = ...;
byte[] b = new byte[...];

int bytesRead;
RowCounter counter = new RowCounter();
while ((bytesRead = is.read(b)) != -1) 
    counter.addBytes(b, 0, bytesRead);

int rowCount = counter.getRowCount();

或者您可以轻松地将其调整为您需要的任何情况。

【讨论】:

以上是关于如何更快地在 byte[] 中搜索字节?的主要内容,如果未能解决你的问题,请参考以下文章

byte[] 数组模式搜索

特定字符搜索

如何在 iOS 应用中更快地搜索本地数据?

如何正确地在 PHP 中对字符串进行 URL 编码?

如何从字节数组创建位图?

如何更快地在 Java 中创建/复制对象? [复制]