Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slow write performance for abstract array #76

Open
johnnychen94 opened this issue Apr 20, 2022 · 10 comments
Open

slow write performance for abstract array #76

johnnychen94 opened this issue Apr 20, 2022 · 10 comments

Comments

@johnnychen94
Copy link
Contributor

johnnychen94 commented Apr 20, 2022

using TestImages, FileIO, ImageCore
using BenchmarkTools

img = Float64.(testimage("cameraman"))

@btime save("tmp.tiff", img) #  1.840 ms (140 allocations: 519.67 KiB)
@btime save("tmp.tiff", reinterpret(Gray{Float64}, img)) # 20.758 ms (235 allocations: 2.01 MiB)
julia> versioninfo()
Julia Version 1.7.2
Commit bf53498635 (2022-02-06 15:21 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: 12th Gen Intel(R) Core(TM) i9-12900K
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-12.0.1 (ORCJIT, goldmont)
@tlnagy
Copy link
Owner

tlnagy commented Apr 21, 2022

That's a wild performance drop. My best guess is that there's an inference failure?

@johnnychen94
Copy link
Contributor Author

This seems to be version related

Julia Version type time
1.6.5 Array 7.388 ms (138 allocations: 519.14 KiB)
1.6.5 ReinterpretArray 22.285 ms (238 allocations: 2.01 MiB)
1.6.5 MappedArray 10.035 ms (236 allocations: 2.01 MiB)
1.7.2 Array 1.498 ms (138 allocations: 519.14 KiB)
1.7.2 ReinterpretArray 19.275 ms (238 allocations: 2.01 MiB)
1.7.2 MappedArray 7.658 ms (235 allocations: 2.01 MiB)
1.8.0-beta1 Array 1.565 ms (181 allocations: 520.90 KiB)
1.8.0-beta1 ReinterpretArray 7.637 ms (234 allocations: 2.01 MiB)
1.8.0-beta1 MappedArray 6.346 ms (234 allocations: 2.01 MiB)
  • MappedArray: of_eltype(Gray{Float64}, img)
  • ReinterpretArray: reinterpret(Gray{Float64}, img)

The ReinterpretArray performance might be due to JuliaLang/julia#43287, which was fixed in Julia 1.8. This also causes ImageMagick's performance drop for JPEG JuliaIO/ImageMagick.jl#208

@tlnagy
Copy link
Owner

tlnagy commented Apr 25, 2022

Thanks for thoroughly checking this. Glad to hear that this was fixed upstream. Can I go ahead and close?

@johnnychen94
Copy link
Contributor Author

johnnychen94 commented Apr 26, 2022

Given that eagerly collecting the data into contiguous memory takes less than 1ms in the same machine, I think there's more room for optimization to further close the gap.

julia> @btime collect(reinterpret(Gray{Float64}, $img));
  251.000 μs (2 allocations: 2.00 MiB)

@tlnagy
Copy link
Owner

tlnagy commented Apr 26, 2022

Writing to disk will almost always have orders of magnitude of slowdown. I'm sure there's performance to be had though.

@johnnychen94
Copy link
Contributor Author

I mean, if save(file, img) is slower than save(file, collect(img)), then it indicates that we're hitting a slow method.

@tlnagy
Copy link
Owner

tlnagy commented Apr 26, 2022

That's fair.

@tlnagy
Copy link
Owner

tlnagy commented Sep 27, 2022

I ran into this performance slowdown again today, it appears to be especially egregious when using memory-mapped OMETIFFs and leads to ~100x slowdown versus writing the image lazily to disk frame-by-frame.

@tlnagy
Copy link
Owner

tlnagy commented Dec 10, 2022

After looking into this more, I think this is mostly a base Julia problem (correct me if I'm wrong here 😅), but even using Base.write leads to this delta.

Base case

julia> img = Float64.(testimage("cameraman"));

julia> @btime write("tmp.tiff", img)
  287.581 μs (13 allocations: 728 bytes)
2097152

Gray type

julia> b = reinterpret(Gray{Float64}, img);

julia> @btime write("tmp.tiff", b) 
ERROR: MethodError: no method matching write(::IOStream, ::Gray{Float64})
Closest candidates are:
  write(::IO, ::Any) at io.jl:672
  write(::IO, ::Any, ::Any...) at io.jl:673
  write(::TiffFile, ::Any) at ~/.julia/packages/TiffImages/FEcMg/src/files.jl:117
  ...
Stacktrace:
  [1] write(io::IOStream, x::Gray{Float64})
    @ Base ./io.jl:672


julia> @btime write("tmp.tiff", reinterpret(UInt8, b)) # need to reinterpret
  31.452 ms (14 allocations: 776 bytes)
2097152

Now, I'm forced to do the extra reinterpret because of issue highlighted here: JuliaLang/julia#24234 (comment) and it seems that reinterpret arrays are slow at writing: JuliaLang/julia#39781

Now interestingly if you reinterpret back to a Float64 instead of UInt8, it's faster

julia> @btime write("tmp.tiff", reinterpret(Float64, b))
  286.599 μs (13 allocations: 728 bytes)
2097152

But that doesn't generalize well:

julia> img = RGB{Float64}.(testimage("cameraman")); # RGB colors this time

julia> @btime write("tmp.tiff", img) # takes about three times longer as expected
  878.243 μs (13 allocations: 728 bytes)
6291456

julia> @btime write("tmp.tiff", reinterpret(Float64, img)) # now this is slow again
  21.970 ms (786446 allocations: 12.00 MiB)
6291456

@vtjnash
Copy link

vtjnash commented Oct 27, 2023

Fixed by JuliaLang/julia#42593?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants