Скопируйте огромный файл с Юлией Mmap - PullRequest
0 голосов
/ 06 марта 2019

У меня есть большой файл (75 ГБ), отображенный в массиве d, который я хочу скопировать в другой m. Поскольку у меня нет 75 ГБ оперативной памяти, я сделал:

for (i,v) in enumerate(d)
    m[i] = v
end

Чтобы скопировать значение файла после значения. Но я получаю скорость копирования ~ 2 МБ / с на SSD, где я ожидаю не менее 50 МБ / с как при чтении, так и при записи.

Как я могу оптимизировать скорость копирования?

=== [править] ===

Согласно комментариям я изменил свой код на следующий, что увеличило скорость записи до 15 МБ / с

function copydcimg(m::Array{UInt16,4}, d::Dcimg)
    m .= d
    Mmap.sync!(m)
end

copydcimg(m,d)

На данный момент, я думаю, мне следует оптимизировать код Dcimg. Этот двоичный файл состоит из кадров, разделенных временной меткой. Вот код, который я использую для доступа к фреймам:

module dcimg

using Mmap
using TOML

struct Dcimg <: AbstractArray{UInt16,4} # struct allowing to access dcimg file
    filename::String # filename of the dcimg
    header::Int # header size in bytes
    clock::Int # clock size in bytes
    x::Int
    y::Int
    z::Int
    t::Int
    m # linear memory map
    Dcimg(filename, header, clock, x, y, z, t) =
      new(filename, header, clock, x, y, z, t,
        Mmap.mmap(open(filename), Array{UInt16, 3},
            (x*y+clock÷sizeof(UInt16), z, t), header)
        )
end

# following functions allows to access DCIMG like an Array
Base.size(D::Dcimg) = (D.x, D.y, D.z, D.t)
# skip clock
Base.getindex(D::Dcimg, i::Int) =
    D.m[i + (i ÷ (D.x*D.y))*D.clock÷sizeof(UInt16)] 
Base.getindex(D::Dcimg, x::Int, y::Int, z::Int, t::Int) =
    D[x + D.x*((y-1) + D.y*((z-1) + D.z*(t-1)))]    

# allowing to automatically parse size
function Dcimg(pathtag)
    p = TOML.parsefile(pathtag * ".toml")
    return Dcimg(pathtag * ".dcimg",
        # ...
        )
end

export Dcimg, getframe

end

1 Ответ

1 голос
/ 07 марта 2019

Я понял! Решение состояло в том, чтобы скопировать фрагмент файла, скажем, по фрагменту (примерно 1024 × 720 UInt16). Таким образом, я достиг 300 МБ / с, чего я даже не знал, что это возможно в одном потоке. Вот код.

В модуле dcimg я добавил методы для доступа к файлу покадрово

# get frame number n (starting form 1)
getframe(D::Dcimg,n::Int) = 
    reshape(D.m[
        D.x*D.y*(n-1)+1 + (n-1)*D.clock÷sizeof(UInt16) : # cosmetic line break
        D.x*D.y*n + (n-1)*D.clock÷sizeof(UInt16)
        ], D.x, D.y)
# get frame for layer z, time t (starting from 1)
getframe(D::Dcimg,z::Int,t::Int) = 
    getframe(D::Dcimg,(z-1)+D.z*(t-1))

Перебор кадров внутри цикла

function copyframes(m::Array{UInt16,4}, d::Dcimg)
    N = d.z*d.t
    F = d.x*d.y
    for i in 1:N
        m[(i-1)*F+1:i*F] = getframe(d, i)
    end
end

copyframes(m,d)

Спасибо всем в комментариях за то, что привели меня к этому.

===== edit =====

для дальнейшего чтения, вы можете посмотреть:

, которые дают подсказки об оптимальном размере блока для копирования за один раз.

...