如何在Ruby中有效地切片二进制数据?

时间:2017-01-02 19:52:24

标签: ruby

在审核了SO Ruby: Split binary data后,我使用了以下代码。

zh

然而,它很慢:

z = 'A' * 1_000_000
z.bytes.each_slice( STREAMING_CHUNK_SIZE ).each do | chunk | 
  c = chunk.pack( 'C*' )
end

98ms来切片并打包1MB文件。这很慢。

使用案例
服务器从外部API接收二进制数据,并使用Benchmark.realtime do ... => 0.0983949700021185 对其进行流式传输 数据预计在50KB到5MB之间,平均为500KB。

那么,如何在Ruby中有效地分割二进制数据?

2 个答案:

答案 0 :(得分:5)

注释

您的代码看起来不错,使用正确的Ruby方法和正确的语法,但它仍然是:

  • 创建一个巨大的整数数组
  • 在多个阵列中切割这个大数组
  • 将这些数组打包回String

替代

以下代码直接从字符串中提取部分,而不进行任何转换:

def get_binary_chunks(string, size)
  Array.new(((string.length + size - 1) / size)) { |i| string.byteslice(i * size, size) }
end

(string.length + size - 1) / size)只是为了避免错过最后一个块,如果它小于size

性能

使用500kB的pdf文件和12345字节的块,Fruity返回:

Running each test 16 times. Test will take about 28 seconds.
_eric_duminil is faster than _b_seven by 380x ± 100.0

get_binary_chunks在此示例中也比StringIO#each(n)快6倍。

进一步优化

如果你确定字符串是二进制的(不是带有'ä'等多字节字符的UTF8),你可以使用slice而不是byteslice

def get_binary_chunks(string, size)
  Array.new(((string.length + size - 1) / size)) { |i| string.slice(i * size, size) }
end

使代码更快(与您的方法相比约为500倍)。

如果您将此代码与Unicode字符串一起使用,则这些字符块将包含size个字符,但字节可能超过size

直接使用块

最后,如果您对获取字符串数组不感兴趣,可以直接使用这些块:

def send_binary_chunks(socket, string, size)
  ((string.length + size - 1) / size).times do |i|
    socket.write string.slice(i * size, size)
  end
end

答案 1 :(得分:3)

StringIO#each(n)与包含BINARY编码的字符串一起使用

require 'stringio'
string.force_encoding(Encoding::BINARY)
StringIO.new(string).each(size) { |chunk| socket.write(chunk) }

这只是在将中间数组推送到套接字之前分配它们。