Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Julia中沿布尔数组的轴进行按位或归约?_Julia - Fatal编程技术网

如何在Julia中沿布尔数组的轴进行按位或归约?

如何在Julia中沿布尔数组的轴进行按位或归约?,julia,Julia,我正试图找到最好的方法,在Julia中将3D布尔掩码数组按位或缩减为2D 当然,我总是可以编写for循环: x = randbool(3,3,3) out = copy(x[:,:,1]) for i = 1:3 for j = 1:3 for k = 2:3 out[i,j] |= x[i,j,k] end end end 但是我想知道是否有更好的方法来减少 out = x[:,:,1] | x[:,:,2] | x[:

我正试图找到最好的方法,在Julia中将3D布尔掩码数组按位或缩减为2D

当然,我总是可以编写for循环:

x = randbool(3,3,3)
out = copy(x[:,:,1])
for i = 1:3
    for j = 1:3
        for k = 2:3
            out[i,j] |= x[i,j,k]
        end
    end
end

但是我想知道是否有更好的方法来减少

out = x[:,:,1] | x[:,:,2] | x[:,:,3]
但我做了一些基准测试:

function simple(n,x)
    out = x[:,:,1] | x[:,:,2]
    for k = 3:n
        @inbounds out |= x[:,:,k]
    end
    return out
end

function forloops(n,x)
    out = copy(x[:,:,1])
    for i = 1:n
        for j = 1:n
            for k = 2:n
                @inbounds out[i,j] |= x[i,j,k]
            end
        end
    end
    return out
end

function forloopscolfirst(n,x)
    out = copy(x[:,:,1])
    for j = 1:n
        for i = 1:n
            for k = 2:n
                @inbounds out[i,j] |= x[i,j,k]
            end
        end
    end
    return out
end

shorty(n,x) = |([x[:,:,i] for i in 1:n]...)

timholy(n,x) = any(x,3)

function runtest(n)
    x = randbool(n,n,n)

    @time out1 = simple(n,x)
    @time out2 = forloops(n,x)
    @time out3 = forloopscolfirst(n,x)
    @time out4 = shorty(n,x)
    @time out5 = timholy(n,x)

    println(all(out1 .== out2))
    println(all(out1 .== out3))
    println(all(out1 .== out4))
    println(all(out1 .== out5))
end

runtest(3)
runtest(500)
结果如下

# For 500
simple: 0.039403016 seconds (39716840 bytes allocated)
forloops: 6.259421683 seconds (77504 bytes allocated)
forloopscolfirst 1.809124505 seconds (77504 bytes allocated)
shorty: elapsed time: 0.050384062 seconds (39464608 bytes allocated)
timholy: 2.396887396 seconds (31784 bytes allocated)

因此,我将使用
simple
shorty

可以应用各种标准的优化技巧和提示,但这里要做的关键观察是Julia按顺序组织数组。对于小尺寸阵列,这不容易看到,但当阵列变大时,这就说明了问题。有一种方法reduce,它经过优化以在集合上执行功能(在本例中为),但它是有代价的。如果组合步骤的数量相对较少,则最好简单地循环。在所有情况下,最小化内存访问次数总的来说更好。下面是利用这两件事进行优化的各种尝试

各种尝试和观察 初始函数 这里有一个函数,它以您的示例为例并对其进行了推广

function boolReduce1(x)
   out = copy(x[:,:,1])
   for i = 1:size(x,1)
       for j = 1:size(x,2)
           for k = 2:size(x,3)
               out[i,j] |= x[i,j,k]
           end
       end
   end
   out
end
创建一个相当大的阵列,我们可以计算它的性能

julia> @time boolReduce1(b); 
elapsed time: 42.372058096 seconds (1056704 bytes allocated)
应用优化 下面是另一个类似的版本,但带有标准类型提示,使用@inbounds并反转循环

function boolReduce2(b::BitArray{3})
   a = BitArray{2}(size(b)[1:2]...)
   for j = 1:size(b,2)
      for i = 1:size(b,1)
         @inbounds a[i,j] = b[i,j,1]
         for k = 2:size(b,3)
            @inbounds a[i,j] |= b[i,j,k]
         end
      end
   end
   a
end
慢慢来

julia> @time boolReduce2(b);
elapsed time: 12.892392891 seconds (500520 bytes allocated)
洞察力 第二个函数要快得多,而且由于没有创建临时数组,所以分配的内存也更少。但是,如果我们简单地取第一个函数并反转数组索引,会怎么样

function boolReduce3(x)
   out = copy(x[:,:,1])
   for j = 1:size(x,2)
       for i = 1:size(x,1)
           for k = 2:size(x,3)
               out[i,j] |= x[i,j,k]
           end
       end
   end
   out
end
现在慢慢来

julia> @time boolReduce3(b);
elapsed time: 12.451501749 seconds (1056704 bytes allocated)
这和第二个函数一样快

使用reduce 我们可以使用一个名为reduce的函数来消除第三个循环。它的功能是使用上一个操作的结果对所有元素重复应用一个操作。这正是我们想要的

function boolReduce4(b)
   a = BitArray{2}(size(b)[1:2]...)
   for j = 1:size(b,2)
      for i = 1:size(b,1)
         @inbounds a[i,j] = reduce(|,b[i,j,:])
     end
   end
   a
end
现在慢慢来

julia> @time boolReduce4(b);
elapsed time: 15.828273008 seconds (1503092520 bytes allocated, 4.07% gc time)
没关系,但速度甚至不如简单的优化原版。原因是,看看分配的所有额外内存。这是因为必须从各地复制数据,以生成用于reduce的输入

结合事物 但是,如果我们尽可能最大限度地发挥洞察力,会怎么样呢。第一个索引不是减少最后一个索引,而是

function boolReduceX(b)
   a = BitArray{2}(size(b)[2:3]...)
   for j = 1:size(b,3)
      for i = 1:size(b,2)
         @inbounds a[i,j] = reduce(|,b[:,i,j])
      end
   end
   a
end
现在创建一个类似的数组并计时

julia> c = randbool(200,2000,2000);

julia> @time boolReduceX(c);
elapsed time: 1.877547669 seconds (927092520 bytes allocated, 21.66% gc time)
使大型阵列的功能比原始版本快20倍。很好

但是如果中等尺寸呢? 如果大小非常大,则上面的函数看起来最好,但如果数据集大小较小,则使用reduce不会产生足够的回报,下面的函数会更快。包括版本2中的临时变速器。boolReduceX的另一个版本使用循环而不是reduce(此处未显示),速度更快

function boolReduce5(b)
   a = BitArray{2}(size(b)[1:2]...)
   for j = 1:size(b,2)
      for i = 1:size(b,1)
         @inbounds t = b[i,j,1]
         for k = 2:size(b,3)
            @inbounds t |= b[i,j,k]
         end
         @inbounds a[i,j] = t
      end
   end
   a
end

julia> b = randbool(2000,2000,20);
julia> c = randbool(20,2000,2000);

julia> @time  boolReduceX(c);
elapsed time: 1.535334322 seconds (799092520 bytes allocated, 23.79% gc time)

julia> @time  boolReduce5(b);
elapsed time: 0.491410981 seconds (500520 bytes allocated)
尝试
任何(x,3)
。只需在此处多输入一点,这样StackOverflow就不会禁止此响应。

开发更快。这只是你想投入多少工作的问题。naïve-devectorized方法速度较慢,因为它是一个位数组:提取连续区域和按位或两者都可以一次在64位块上完成,但naïve-devectorized方法一次操作一个元素。最重要的是,索引位数组的速度很慢,这既是因为涉及到一系列位操作,也是因为由于边界检查,它目前无法内联。这里有一个被开发的策略,但它利用了位数组的结构。大部分代码都是从copy_块复制粘贴的!在bitarray.jl中,我没有试图美化它(对不起!)


记录在案,
b
上的那些类型提示对性能没有帮助。@IainDunning-Hehe,是的,我看到了。我将从最后两个版本中删除它们,但将其保留在第二个版本中以说明这一点。@waTeim并在调用reduce时使用ArrayView->view使boolReduceX更快:)我迫不及待地等待,直到它在v0.4中出现,只需警告您需要将位数组转换为数组{Bool}@waTeim,我喜欢您答案的深度。如果你把Iain的一些测试作为比较,我会转而接受你的测试。干杯我现在已经将这个答案纳入了我的基准测试中。绝对是最短的,但不是最快的(令人惊讶!)这是一个很好的例子,说明了矢量化是如何发挥作用的。关键的一点是,由于位数组是用向量{Uint64}中的位表示的,所以向量化版本大大减少了您必须执行的位提取量。谢谢,Iain!我喜欢最好的答案也是最短的答案之一。在我看来,分配的字节数和运行时间之间的相关性要强得多。有时这可能仍然是正确的,但我肯定必须更频繁地检查这一假设。我的头脑中也有这种关联,但回答一些堆栈溢出问题已经稍微改变了我的观点。
function devec(n::Int, x::BitArray)
    src = x.chunks
    out = falses(n, n)
    dest = out.chunks
    numbits = n*n

    kd0 = 1
    ld0 = 0
    for j = 1:n
        pos_s = (n*n)*(j-1)+1
        kd1, ld1 = Base.get_chunks_id(numbits - 1)
        ks0, ls0 = Base.get_chunks_id(pos_s)
        ks1, ls1 = Base.get_chunks_id(pos_s + numbits - 1)

        delta_kd = kd1 - kd0
        delta_ks = ks1 - ks0

        u = Base._msk64
        if delta_kd == 0
            msk_d0 = ~(u << ld0) | (u << (ld1+1))
        else
            msk_d0 = ~(u << ld0)
            msk_d1 = (u << (ld1+1))
        end
        if delta_ks == 0
            msk_s0 = (u << ls0) & ~(u << (ls1+1))
        else
            msk_s0 = (u << ls0)
        end

        chunk_s0 = Base.glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0)

        dest[kd0] |= (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0)

        delta_kd == 0 && continue

        for i = 1 : kd1 - kd0
            chunk_s1 = Base.glue_src_bitchunks(src, ks0 + i, ks1, msk_s0, ls0)

            chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0)

            dest[kd0 + i] |= chunk_s

            chunk_s0 = chunk_s1
        end
    end

    out
end
simple: 0.051321131 seconds (46356000 bytes allocated, 30.03% gc time)
forloops: 6.226652258 seconds (92976 bytes allocated)
forloopscolfirst: 2.099381939 seconds (89472 bytes allocated)
shorty: 0.060194226 seconds (46387760 bytes allocated, 36.27% gc time)
timholy: 2.464298752 seconds (31784 bytes allocated)
devec: 0.008734413 seconds (31472 bytes allocated)