【问题标题】:Elixir - Replace at position in binaryElixir - 在二进制位置替换
【发布时间】:2018-02-28 04:26:11
【问题描述】:

问题

我正在尝试在 Elixir 中制作一个基本的图像(位图)书写器,但我坚持了一点。

我尝试制作一个函数来将像素设置为二进制文件。我使用模式匹配,但我的功能显然太慢了(超过 10 分钟才能将所有像素设置为大小为 1024 * 768 的图片)。

目前,我有一个大小等于宽度 * 高度的二进制文件。就像您在下面的代码中看到的那样,我的函数将 x 和 y 作为参数,并且必须在此位置修改一个 int。


当前代码

# Function
def replace_by_test(output, width, x, y) do
    out_offset = y * width + x
    <<
        o_before :: binary-size(out_offset),
        _ :: binary-size(4),
        o_after :: binary
    >> = output

    << o_before :: binary, "TEST" :: binary, o_after :: binary >>
end

# Test on a 1024 * 768 resolution image
out_size = 1024 * 768 * 8
output = << 0 :: size(out_size) >>
for x <- 0..(1024*768-1), do: replace_by_test(output, 1024, 0, 0)

目标

让这段代码更快。如果可能,请在 10 秒内运行它。

【问题讨论】:

  • 你想用这个实现什么?您在这里创建了一个 786 kb 字符串的副本 786k 次。我认为您无法显着加快速度。对于您要解决的问题,也许有更好的解决方案?
  • @Dogbert 我只是希望能够从像素序列创建图像(例如 List)
  • 列表中的像素是连续的吗?例如。 x=0 y=0 然后x=0 y=1 然后x=0 y=3 等等?还是可以随机排列?
  • 随机顺序
  • 您是否正在尝试进行某种隐写术?像@Dogbert 一样,我很难理解你真正想要做什么。

标签: elixir


【解决方案1】:

像这样构造一个新的二进制文件会导致 Erlang 在每次迭代时都必须复制整个二进制文件。复制 786432 字节 786432 次肯定会很慢(这是需要分配的 618GB 内存,然后很快就会释放!)。您需要为此使用不同的数据结构。

一种选择是在构建二进制文件时使用 Map,然后在完成修改后将其转换为二进制文件:

# Create a blank image.
map = for x <- 1..1024, y <- 1..768, into: %{}, do: {{x, y}, 0}

# Set each pixel once to x * y
map =
  0..1023
  |> Enum.reduce(map, fn x, map ->
    0..767 |> Enum.reduce(map, fn y, map ->
      Map.put(map, {x, y}, 0)
    end)
  end)

# Get back a binary
binary = for x <- 0..1023, y <- 0..767, into: <<>>, do: <<Map.get(map, {x, y})>>

IO.inspect binary
IO.inspect byte_size(binary)

输出:

<<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>
786432

整个脚本在我的机器上执行大约需要 2.5 秒。

另一种选择是使用Erlang's array module。它可能比地图更快。阅读该模块的文档后应该很容易实现。

编辑:这是转换为array 的代码。它在我的机器上运行几乎正好 1 秒,比 map 快 2.5 倍。

# Create a blank image.
array = :array.new(size: 1024 * 768, default: 0)

# Set each pixel once to x * y
array =
  0..1023
  |> Enum.reduce(array, fn x, array ->
    0..767 |> Enum.reduce(array, fn y, array ->
      :array.set(y * 1024 + x, 123, array)
    end)
  end)

# Get back a binary
binary = :array.foldl(fn _, x, acc -> <<acc::binary, x>> end, <<>>, array)

【讨论】:

    猜你喜欢
    • 2018-10-26
    • 2011-02-05
    • 2020-07-13
    • 1970-01-01
    • 2019-06-23
    • 2019-04-21
    • 1970-01-01
    • 2020-02-13
    • 2017-08-21
    相关资源
    最近更新 更多