【问题标题】:De-vectorizing array manipulation in JuliaJulia 中的去向量化数组操作
【发布时间】:2021-03-06 19:19:49
【问题描述】:

这个问题是one of my previous questions 的扩展,内容是在 Python 中优化数组计算。我给定问题的答案是通过@tomjn as 给出的

tot_list = np.where((X == 1) | (Y == 1) | (Z == 1),np.random.random(),1).sum(axis=1)

现在,我想用 Julia 写这个。上面很容易翻译成

tot_list = ifelse.((X.==1).|(Y.==1).|(Z.==1),rand(Float64),1)

请注意,X、Y 和 Z 是逻辑矩阵。当我在 Julia 中运行我的代码时,上述工作正常。但是,它非常缓慢。我正在尝试对上述内容进行去矢量化,我得到了

tot_list = similar(X)
for j in 1:length(tot_list)
    tot_list[j] = ifelse((X[j]==1)|(Y[j]==1)|(Z[j]==1),rand(Float64),1)
end

但是,生成的代码实际上不会产生与矢量化、低效版本相同的结果。我无法举一个例子,因为差异仅在非常大的输入时才明显,但我很肯定上面编写的 Julia 代码的两个部分之间存在很大差异。我究竟做错了什么?两个tot_list不应该一样吗?

编辑:差异可能是由于我在执行此计算之后尝试进行的计算。在我运行上面的代码之前,我定义了

variable_tot = zeros(Float64,1,8*N_state)

运行上面给出的 tot_list 计算后,我再运行

variable_tot=variable_tot+reshape(tot_list,:,length(tot_list))

这和我的问题有关吗?

【问题讨论】:

  • XYZ 是非const 全局变量吗?如果是这样,只需将您的代码包装在一个函数中就会有很大帮助。
  • @OscarSmith 它们不是全局变量。我定义了一个函数,并在该函数中专门将它们定义为更大矩阵的子集。具体来说,我称一个大的 nxm 逻辑矩阵,提取某些列并称它们为 R1 和 R2,设置 X=R1、Y=R1|R2 和 Z=R1&R2。

标签: python arrays logic julia vectorization


【解决方案1】:

怎么样(用ifelse编辑,这对我来说更快)

R = rand(size(X)...)  # generate all the random values at once
C = @. X | Y | Z      # the condition
@. ifelse(C, R, 1.0)  # the output

它生成所有随机值(即使是那些你不会使用的随机值,但它会一举完成,我认为这比循环遍历rand() 更快),分配条件C,然后应用@ 987654325@.

编辑2,添加比较:这个解决方案分配更多,但对我来说更快一点:

julia> @btime ((x) -> x ? rand() : 1.0).($X .| $Y .| $Z) ;
  5.598 ms (2 allocations: 7.63 MiB)

julia> @btime (R = rand(size($X)...) ; C = @. $X | $Y | $Z ; @. ifelse(C, R, 1.0)) ;
  3.739 ms (8 allocations: 15.38 MiB)

【讨论】:

  • 不,抱歉,我的去矢量化版本遇到了同样的问题。我认为存在细微差别,这可能是由于我的求和方式(请参阅已编辑的问题),但我不确定。
  • 不清楚你想做什么。您的总和仅出现在 python 代码中。如果我理解正确,XYZ 是二维数组,tot_list 是一维行数组,它在第一维上对“1 或 rand”二维数组求和?所以你需要做tot_list = sum(O, dims=1) where O = @. ifelse(C, R, 1.0)。不确定您的 N_state 值是什么或为什么要重塑 tot_list?此外,如果您将其初始化为 zeros ,添加它不会有任何作用。如果您需要将列向量重塑为行向量,反之亦然,我会这样做 transpose(x)x'permutedims(x)
【解决方案2】:

也许试试这个代码:

((x) -> x ? rand() : 1.0).(X .| Y .| Z)

请注意,对于 Julia 函数而言,类型稳定对于性能至关重要。由于rand() 正在返回Float64,因此替代1 值也需要为Float64 - 因此使用1.0 而不是1

性能测试 - 我的机器上 1'000'000 个元素矩阵需要 5 毫秒:

X = rand(Bool, 1000, 1000)
Y = rand(Bool, 1000, 1000)
Z = rand(Bool, 1000, 1000)

julia> using BenchmarkTools

julia> @btime ((x) -> x ? rand() : 1.0).($X .| $Y .| $Z)
  5.615 ms (2 allocations: 7.63 MiB)

【讨论】:

  • 不幸的是,它给我的结果与我的矢量化代码相同。给我一点时间;让我编辑我的问题以包含有关我的具体计算的更多信息。编辑:好的,我的问题已被编辑以包含更多我的代码。这可能是由于我在进行主要计算后如何操作 tot_list ......
  • 只是一个简短的说明;根据您的回答,我将代码更改为 tot_list = ifelse.((X.==1).|(Y.==1).|(Z.==1),rand(Float64),1.0) ,我立即得到了加速。谢谢您的建议。不过,我还是很好奇为什么这个sn-p的代码和你的更干净的版本还有区别。
  • 部分原因是ifelse 评估了两个输入。这意味着您拨打rand 的方式超出了必要范围。
  • 也可能是因为您使用X == 1 而不仅仅是X
猜你喜欢
  • 1970-01-01
  • 2013-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多