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

Add ImageIO for normal TIFF files #290

Merged
merged 2 commits into from
Apr 16, 2021
Merged

Add ImageIO for normal TIFF files #290

merged 2 commits into from
Apr 16, 2021

Conversation

IanButterworth
Copy link
Member

@IanButterworth IanButterworth commented Feb 21, 2021

Thanks to @tlnagy's https://github.com/tlnagy/TiffImages.jl, which is now added into https://github.com/JuliaIO/ImageIO.jl as of v0.5.0, we now have native normal TIFF IO in julia that we can use in FileIO 🎉

@tlnagy do you have a feel for performance, both IO and load time? Are we ok placing this first for TIFFs?

@coveralls
Copy link

Coverage Status

Coverage remained the same at 74.487% when pulling e55aafd on ib/tiff_imageio into 1df5033 on master.

@tlnagy
Copy link
Contributor

tlnagy commented Feb 22, 2021

Here are some benchmarks for TIFFs on latest master versus ImageMagick v1.1.6 on the TIFFs from https://github.com/tlnagy/exampletiffs

timing

TiffImages can't open bali.tif because it doesn't support that type of compression. For the cases that are close, those are images using packbits compression and I haven't yet fixed tlnagy/TiffImages.jl#19. Making that switch should dramatically speed up TiffImages in those tests. Can't let ImageMagick get too close 😝

TLDR: For many smaller, uncompressed TIFFs, TiffImages can be ~10x faster than ImageMagick.

@IanButterworth
Copy link
Member Author

IanButterworth commented Feb 22, 2021

Nice!
I also did some benchmarks, with the full FileIO + ImageIO (with TiffImages) setup

Looks like TiffImages is a little slower to load (understandable, given there's probably more compilation going on)
Load is much faster across different image sizes!

Save though gets a fair bit slower for larger image sizes

ImageMagick

Gray{N0f8} 10x10 ===
First save (including package load): 0.908397 seconds (2.51 M allocations: 143.289 MiB, 4.90% gc time)
Save: 680.148 μs (974 allocations: 64.27 KiB)
First load: 0.651449 seconds (696.03 k allocations: 40.396 MiB, 1.33% gc time, 54.22% compilation time)
Load:  6.237 ms (1079 allocations: 68.80 KiB)

Gray{N0f8} 100x100 ===
Save: 823.838 μs (974 allocations: 93.52 KiB)
Load: 6.380 ms (1080 allocations: 98.06 KiB)

Gray{N0f8} 1000x1000 ===
Save: 11.444 ms (977 allocations: 2.92 MiB)
Load: 17.843 ms (1087 allocations: 2.93 MiB)

QuartzImageIO

Gray{N0f8} 10x10 ===
First save (including package load): 0.647274 seconds (1.66 M allocations: 100.137 MiB, 5.02% gc time)
Save: 753.736 μs (555 allocations: 36.39 KiB)
First load: 1.265828 seconds (4.18 M allocations: 253.270 MiB, 5.62% gc time)
Load: 772.830 μs (704 allocations: 45.06 KiB)

Gray{N0f8} 100x100 ===
Save: 960.318 μs (555 allocations: 65.64 KiB)
Load: 854.396 μs (704 allocations: 64.56 KiB)

Gray{N0f8} 1000x1000 ===
Save: 6.783 ms (558 allocations: 2.90 MiB)
Load: 4.332 ms (706 allocations: 1.95 MiB)

ImageIO:

Gray{N0f8} 10x10 ===
First save (including package load): 1.231274 seconds (2.37 M allocations: 140.034 MiB, 3.23% gc time)
Save: 228.185 μs (287 allocations: 13.66 KiB)
First load: 1.943663 seconds (6.55 M allocations: 379.587 MiB, 6.05% gc time, 0.42% compilation time)
Load: 278.932 μs (394 allocations: 18.38 KiB)

Gray{N0f8} 100x100 ===
Save: 1.023 ms (311 allocations: 23.78 KiB)
Load: 392.603 μs (407 allocations: 38.08 KiB)

Gray{N0f8} 1000x1000 ===
Save: 72.959 ms (312 allocations: 990.55 KiB)
Load: 2.966 ms (415 allocations: 1.93 MiB)

Code used

import Pkg

Pkg.develop(path=joinpath(@__DIR__,"..","FileIO.jl"))
Pkg.develop(path=joinpath(@__DIR__,"..","TiffImages.jl"))
Pkg.add("ImageCore")
Pkg.add("BenchmarkTools")

using ImageCore, FileIO, BenchmarkTools

tmp, _ = mktemp()
fpath = string(tmp,".tiff")

for backend in ["ImageMagick", "QuartzImageIO", "ImageIO"]
    Pkg.add(backend)
    @info backend
    for typ in [Gray{N0f8}, RGB{N0f8}]
        for n in 1:3
            println("$typ $(10^n)x$(10^n) ===")
            img = rand(typ, 10^n, 10^n)
            if n == 1
                print("First save:")
                @time FileIO.save(fpath, img)
            end
            print("Save:")
            @btime FileIO.save($fpath, $img)

            if n == 1
                print("First load:")
                @time img2 = FileIO.load(fpath)
            end
            print("Load:")
            @btime FileIO.load($fpath)
        end
    end
    Pkg.rm(backend)
end

@IanButterworth
Copy link
Member Author

Switching to PermutedDimsArray in tlnagy/TiffImages.jl#33 helps save a little

ImageiO with tlnagy/TiffImages.jl#33

Gray{N0f8} 10x10 ===
First save (including package load): 1.267531 seconds (2.84 M allocations: 167.214 MiB, 3.30% gc time)
Save:  184.759 μs (282 allocations: 13.25 KiB)
First load: 2.020210 seconds (7.06 M allocations: 411.276 MiB, 5.36% gc time, 14.77% compilation time)
Load: 209.573 μs (394 allocations: 18.38 KiB)

Gray{N0f8} 100x100 ===
Save: 684.892 μs (306 allocations: 13.62 KiB)
Load: 237.791 μs (407 allocations: 38.08 KiB)

Gray{N0f8} 1000x1000 ===
Save: 52.405 ms (306 allocations: 13.62 KiB)
Load: 2.555 ms (415 allocations: 1.93 MiB)

@tlnagy
Copy link
Contributor

tlnagy commented Feb 22, 2021

What's going with saving the 1000x1000 image with TiffImages? That seems like a bug.

@IanButterworth
Copy link
Member Author

Yeah. I'd profile it but profiling isn't reliable on MacOS

@tlnagy
Copy link
Contributor

tlnagy commented Feb 22, 2021

Fair enough. I'll profile it and see if I figure out what's going on. I haven't optimized it the same way as load so it probably needs a bit of work.

@IanButterworth
Copy link
Member Author

IanButterworth commented Feb 22, 2021

On quick inspection, it seems like ~95% of the time is spent in https://github.com/JuliaLang/julia/blob/master/base/io.jl#L664

Unless more data is being written than needed, is julia just a lot slower on write io??
I know julia is slower for that on MacOS than Linux, but not that much

@IanButterworth
Copy link
Member Author

IanButterworth commented Feb 22, 2021

Turns out julia's write method doesn't perform well on the reinterpret array types. Doing a collect before sending to write helps drastically

ImageIO with the updated tlnagy/TiffImages.jl#33

Gray{N0f8} 10x10 ===
First save (including package load):  1.338628 seconds (3.01 M allocations: 176.856 MiB, 3.60% gc time)
Save:  182.608 μs (282 allocations: 13.38 KiB)
First load:  1.948858 seconds (7.04 M allocations: 409.948 MiB, 5.25% gc time, 14.53% compilation time)
Load:  203.720 μs (394 allocations: 18.38 KiB)

Gray{N0f8} 100x100 ===
Save:  214.151 μs (306 allocations: 23.50 KiB)
Load:  237.363 μs (407 allocations: 38.08 KiB)

Gray{N0f8} 1000x1000 ===
Save:  2.438 ms (307 allocations: 990.27 KiB)
Load:  2.371 ms (415 allocations: 1.93 MiB)

That's save improved from 72.959 ms down to 2.438 ms for a 1000x1000 image

With that, it's the fastest across the board (except package load, but that's ok)

@tlnagy
Copy link
Contributor

tlnagy commented Feb 22, 2021

Interesting! Though collecting inside the for loop is gonna dramatically increase memory usage for large multi page TIFFs (you can see it happening already in your benchmarks). So it would probably be best to create a cache, realize the array into that, and then write it to disk. Then update the same cache each iteration.

Edit: This is making me think whether the reinterpret call is needed at all. Julia's write should work on non UInt8s and then we might be able to avoid hitting JuliaLang/julia#39781

@timholy
Copy link
Member

timholy commented Feb 22, 2021

I do worry about the case where I have a 1TB mmapped file and I decide to write it as a TIFF. (I always choose NRRD, but still...) Can you batch it in chunks?

@IanButterworth
Copy link
Member Author

I guess two write methods could be provided:

  • current behavior
  • cached collect

Switching between the two could be done with a kwarg, and/or automatically selected based on whether it's mmapped or summarysize(img::DenseTaggedImage) is above a threshold?

Something like write(io::IO, img, collect::Bool=false) seems like the right default?

@timholy
Copy link
Member

timholy commented Feb 22, 2021

That seems reasonable. But it's also probably worth saying that it seems unlikely you'll ever see 1TB in a single planar image, so collecting & writing one image at a time seems like it might be a pretty safe choice.

@tlnagy
Copy link
Contributor

tlnagy commented Feb 23, 2021

I think having that option is unnecessary. TiffImages is not set up to read or write enormous single page TIFFs. The proper way to write data of that size is to strip or tile the data during saving so that other TIFF readers could handle the huge single page. Since TiffImages currently lacks that ability, I think the cache-collect option will handle 99+% of cases.

Multi-page TIFFs being many gigabytes, on the other hand, is quite common so I want to make sure we don't cause too much memory pressure inside the writing for loop.

@IanButterworth
Copy link
Member Author

With tlnagy/TiffImages.jl#33 updated to do the cached-collect it's looking good

ImageIO

Gray{N0f8} 10x10 ===
First save (including package load):  1.573968 seconds (3.71 M allocations: 212.821 MiB, 4.18% gc time)
Save:  192.489 μs (282 allocations: 13.38 KiB)
First load:  2.112263 seconds (7.02 M allocations: 407.205 MiB, 5.21% gc time, 15.36% compilation time)
Load:  212.966 μs (395 allocations: 18.56 KiB)
Gray{N0f8} 100x100 ===
Save:  229.013 μs (306 allocations: 23.50 KiB)
Load:  241.679 μs (408 allocations: 48.02 KiB)
Gray{N0f8} 1000x1000 ===
Save:  2.800 ms (307 allocations: 990.27 KiB)
Load:  1.953 ms (417 allocations: 2.88 MiB)

RGB{N0f8} 10x10 ===
First save:  0.454234 seconds (1.59 M allocations: 88.217 MiB, 3.70% gc time, 76.44% compilation time)
Save:  202.907 μs (312 allocations: 14.67 KiB)
First load:  0.230562 seconds (482.35 k allocations: 28.198 MiB, 4.29% gc time, 46.87% compilation time)
Load:  229.457 μs (435 allocations: 21.30 KiB)
RGB{N0f8} 100x100 ===
Save:  329.011 μs (345 allocations: 44.17 KiB)
Load:  273.730 μs (457 allocations: 108.59 KiB)
RGB{N0f8} 1000x1000 ===
Save:  13.534 ms (345 allocations: 2.88 MiB)
Load:  3.274 ms (463 allocations: 8.60 MiB)

QuartzImageIO

Gray{N0f8} 10x10 ===
First save (including package load):  1.156643 seconds (2.58 M allocations: 151.593 MiB, 5.10% gc time)
Save:  765.629 μs (526 allocations: 35.28 KiB)
First load:  1.698910 seconds (4.37 M allocations: 263.313 MiB, 4.41% gc time, 19.56% compilation time)
Load:  734.756 μs (675 allocations: 43.95 KiB)
Gray{N0f8} 100x100 ===
Save:  897.586 μs (526 allocations: 64.53 KiB)
Load:  768.001 μs (675 allocations: 63.45 KiB)
Gray{N0f8} 1000x1000 ===
Save:  6.555 ms (529 allocations: 2.90 MiB)
Load:  3.949 ms (677 allocations: 1.95 MiB)

RGB{N0f8} 10x10 ===
First save:  0.191422 seconds (641.40 k allocations: 38.038 MiB, 3.27% gc time, 4.38% compilation time)
Save:  762.424 μs (526 allocations: 36.17 KiB)
First load:  0.248198 seconds (1.03 M allocations: 55.236 MiB, 6.05% gc time)
Load:  704.098 μs (678 allocations: 44.58 KiB)
RGB{N0f8} 100x100 ===
Save:  1.019 ms (529 allocations: 152.20 KiB)
Load:  872.723 μs (680 allocations: 102.58 KiB)
RGB{N0f8} 1000x1000 ===
Save:  18.054 ms (529 allocations: 11.48 MiB)
Load:  18.969 ms (682 allocations: 5.76 MiB)

ImageMagick

Gray{N0f8} 10x10 ===
First save (including package load):  0.940514 seconds (2.52 M allocations: 143.106 MiB, 3.38% gc time)
Save:  725.595 μs (968 allocations: 64.66 KiB)
First load:  0.650471 seconds (697.22 k allocations: 40.451 MiB, 1.16% gc time, 52.90% compilation time)
Load:  6.267 ms (1073 allocations: 69.19 KiB)
Gray{N0f8} 100x100 ===
Save:  868.239 μs (968 allocations: 93.91 KiB)
Load:  6.374 ms (1074 allocations: 98.45 KiB)
Gray{N0f8} 1000x1000 ===
Save:  11.733 ms (971 allocations: 2.92 MiB)
Load:  18.852 ms (1081 allocations: 2.93 MiB)

RGB{N0f8} 10x10 ===
First save:  0.354316 seconds (1.38 M allocations: 77.042 MiB, 3.43% gc time, 2.34% compilation time)
Save:  735.419 μs (965 allocations: 65.22 KiB)
First load:  0.176520 seconds (673.67 k allocations: 37.683 MiB, 4.59% gc time)
Load:  768.326 μs (1070 allocations: 69.75 KiB)
RGB{N0f8} 100x100 ===
Save:  905.104 μs (968 allocations: 152.22 KiB)
Load:  1.072 ms (1074 allocations: 156.77 KiB)
RGB{N0f8} 1000x1000 ===
Save:  15.500 ms (968 allocations: 8.65 MiB)
Load:  26.541 ms (1078 allocations: 8.65 MiB)

@timholy
Copy link
Member

timholy commented Feb 24, 2021

Really excited about this! Looks like we might want to give TiffImages.jl a go-over for latency with SnoopCompile, and then I'd be happy promoting it to the default solution for TIFF.

@IanButterworth
Copy link
Member Author

Improvement after been SnoopCompiled in tlnagy/TiffImages.jl#33
Perhaps could be further improved? The scripts are in that PR for review. Maybe it needs work further up the stack in ImageIO or FileIO?

btw, @timholy I love how fancy SnoopCompile has gotten with the package specific files and timing comments!

FileIO > ImageIO > TiffImages (after Snooping)

Gray{N0f8} 10x10 ===
First save (including package load):  1.424578 seconds (3.36 M allocations: 196.196 MiB, 3.92% gc time)
Save:  183.827 μs (282 allocations: 13.38 KiB)
First load:  1.118569 seconds (1.32 M allocations: 77.131 MiB, 1.63% gc time, 90.50% compilation time)
Load:  205.501 μs (420 allocations: 19.83 KiB)
Gray{N0f8} 100x100 ===
Save:  217.661 μs (306 allocations: 23.50 KiB)
Load:  232.268 μs (435 allocations: 39.56 KiB)
Gray{N0f8} 1000x1000 ===
Save:  2.313 ms (307 allocations: 990.27 KiB)
Load:  2.278 ms (443 allocations: 1.93 MiB)

RGB{N0f8} 10x10 ===
First save:  0.347957 seconds (676.66 k allocations: 39.127 MiB, 1.25% gc time, 96.04% compilation time)
Save:  191.645 μs (316 allocations: 14.89 KiB)
First load:  0.116767 seconds (161.37 k allocations: 9.427 MiB, 67.07% compilation time)
Load:  224.369 μs (465 allocations: 22.66 KiB)
RGB{N0f8} 100x100 ===
Save:  311.634 μs (349 allocations: 44.39 KiB)
Load:  262.600 μs (492 allocations: 81.05 KiB)
RGB{N0f8} 1000x1000 ===
Save:  13.750 ms (349 allocations: 2.88 MiB)
Load:  3.150 ms (498 allocations: 5.74 MiB)

FileIO > ImageIO > TiffImages (before Snooping)

Gray{N0f8} 10x10 ===
First save (including package load):  1.573968 seconds (3.71 M allocations: 212.821 MiB, 4.18% gc time)
Save:  192.489 μs (282 allocations: 13.38 KiB)
First load:  2.112263 seconds (7.02 M allocations: 407.205 MiB, 5.21% gc time, 15.36% compilation time)
Load:  212.966 μs (395 allocations: 18.56 KiB)
Gray{N0f8} 100x100 ===
Save:  229.013 μs (306 allocations: 23.50 KiB)
Load:  241.679 μs (408 allocations: 48.02 KiB)
Gray{N0f8} 1000x1000 ===
Save:  2.800 ms (307 allocations: 990.27 KiB)
Load:  1.953 ms (417 allocations: 2.88 MiB)

RGB{N0f8} 10x10 ===
First save:  0.454234 seconds (1.59 M allocations: 88.217 MiB, 3.70% gc time, 76.44% compilation time)
Save:  202.907 μs (312 allocations: 14.67 KiB)
First load:  0.230562 seconds (482.35 k allocations: 28.198 MiB, 4.29% gc time, 46.87% compilation time)
Load:  229.457 μs (435 allocations: 21.30 KiB)
RGB{N0f8} 100x100 ===
Save:  329.011 μs (345 allocations: 44.17 KiB)
Load:  273.730 μs (457 allocations: 108.59 KiB)
RGB{N0f8} 1000x1000 ===
Save:  13.534 ms (345 allocations: 2.88 MiB)
Load:  3.274 ms (463 allocations: 8.60 MiB)

@tlnagy
Copy link
Contributor

tlnagy commented Feb 24, 2021

Snoop compilation PR is now over at tlnagy/TiffImages.jl#35. The effect seems extremely minor, with some increasing/others decreasing. Seems to mostly affect the initial load/save.

@timholy
Copy link
Member

timholy commented Feb 25, 2021

It's not extremely minor: the first load dropped by a factor of 2 (from ~2s to ~1s). That's enough, especially for a newcomer to Julia, to yield a very different subjective experience. And anything called with the same types but with a different size image shouldn't trigger additional compilation, so it's really only this very first usage in a fresh session that matters.

@tlnagy
Copy link
Contributor

tlnagy commented Feb 25, 2021

It's not extremely minor: the first load dropped by a factor of 2 (from ~2s to ~1s). That's enough, especially for a newcomer to Julia, to yield a very different subjective experience.

That's a good point. My head was still stuck in benchmarking land and @btimes 😅

@IanButterworth
Copy link
Member Author

tlnagy/TiffImages.jl#35 rebased after tlnagy/TiffImages.jl#36 merged

FileIO > ImageIO > TiffImages (after tlnagy/TiffImages.jl#36, with Snooping)

Gray{N0f8} 10x10 ===
First save (including package load):  1.439977 seconds (3.37 M allocations: 197.384 MiB, 3.97% gc time)
Save:  179.286 μs (325 allocations: 15.44 KiB)
First load:  1.100683 seconds (929.73 k allocations: 54.180 MiB, 0.63% gc time, 90.95% compilation time)
Load:  188.595 μs (404 allocations: 19.28 KiB)
Gray{N0f8} 100x100 ===
Save:  235.782 μs (326 allocations: 25.20 KiB)
Load:  215.958 μs (414 allocations: 38.94 KiB)
Gray{N0f8} 1000x1000 ===
Save:  2.779 ms (327 allocations: 991.97 KiB)
Load:  2.265 ms (424 allocations: 1.93 MiB)

RGB{N0f8} 10x10 ===
First save (including package load):  0.412107 seconds (1.47 M allocations: 81.024 MiB, 1.88% gc time, 97.63% compilation time)
Save:  196.498 μs (358 allocations: 16.83 KiB)
First load:  0.222927 seconds (160.46 k allocations: 9.241 MiB, 3.47% gc time, 98.96% compilation time)
Load:  204.194 μs (437 allocations: 21.86 KiB)
RGB{N0f8} 100x100 ===
Save:  304.344 μs (362 allocations: 45.88 KiB)
Load:  236.903 μs (451 allocations: 80.05 KiB)
RGB{N0f8} 1000x1000 ===
Save:  12.775 ms (362 allocations: 2.88 MiB)
Load:  2.931 ms (459 allocations: 5.74 MiB)

FileIO > ImageIO > TiffImages (before tlnagy/TiffImages.jl#36, with Snooping, from above comment)

Gray{N0f8} 10x10 ===
First save (including package load):  1.424578 seconds (3.36 M allocations: 196.196 MiB, 3.92% gc time)
Save:  183.827 μs (282 allocations: 13.38 KiB)
First load:  1.118569 seconds (1.32 M allocations: 77.131 MiB, 1.63% gc time, 90.50% compilation time)
Load:  205.501 μs (420 allocations: 19.83 KiB)
Gray{N0f8} 100x100 ===
Save:  217.661 μs (306 allocations: 23.50 KiB)
Load:  232.268 μs (435 allocations: 39.56 KiB)
Gray{N0f8} 1000x1000 ===
Save:  2.313 ms (307 allocations: 990.27 KiB)
Load:  2.278 ms (443 allocations: 1.93 MiB)

RGB{N0f8} 10x10 ===
First save:  0.347957 seconds (676.66 k allocations: 39.127 MiB, 1.25% gc time, 96.04% compilation time)
Save:  191.645 μs (316 allocations: 14.89 KiB)
First load:  0.116767 seconds (161.37 k allocations: 9.427 MiB, 67.07% compilation time)
Load:  224.369 μs (465 allocations: 22.66 KiB)
RGB{N0f8} 100x100 ===
Save:  311.634 μs (349 allocations: 44.39 KiB)
Load:  262.600 μs (492 allocations: 81.05 KiB)
RGB{N0f8} 1000x1000 ===
Save:  13.750 ms (349 allocations: 2.88 MiB)
Load:  3.150 ms (498 allocations: 5.74 MiB)

@timholy
Copy link
Member

timholy commented Feb 25, 2021

Seems like no big changes. Did you regenerate the precompiles, though? The right precompiles are different now.

I'm not surprised that there wouldn't be big changes, the precompilation looked pretty thorough and the biggest exception was non-precompilable OrderedDict methods that my PR also couldn't fix.

Out of curiosity, why do you think it's better to test latency including the package load time? I can see arguments both ways. But one argument for testing without loading is that you might be sensitive to changes that get swamped by package load time.

@IanButterworth
Copy link
Member Author

why do you think it's better to test latency including the package load time?

My thinking was because that's how a FileIO user would experience it. The first save here loads ImageIO and TiffImages

using FileIO
FileIO.save("img.tiff", img)

Did you regenerate the precompiles, though?

I did. I was surprised too.
I too often fall down this rabbit hole, but julia disk IO on macos is known to be slower.. could I just be more dominated by IO?
The test code is here #290 (comment) if anyone wants to double-check

@IanButterworth
Copy link
Member Author

IanButterworth commented Feb 26, 2021

Turns out there's a fair bit of FileIO/ImageIO overhead going on, and my benchmarks have been for FileIO entry points

Gray{N0f8} 10x10 ===
@btime FileIO.save       187.311 μs (334 allocations: 15.70 KiB)
@btime ImageIO.save      136.665 μs (243 allocations: 10.67 KiB)
@btime TiffImages.save   127.046 μs (242 allocations: 10.66 KiB)

@btime FileIO.load       192.159 μs (414 allocations: 19.67 KiB)
@btime ImageIO.load       70.146 μs (256 allocations: 11.67 KiB)
@btime TiffImages.load    65.848 μs (253 allocations: 11.62 KiB)

A few thoughts:

Gray{N0f8} 10x10 ===
@btime FileIO.save       165.095 μs (273 allocations: 12.78 KiB)

@btime FileIO.load       177.397 μs (369 allocations: 17.25 KiB)
  • FileIO uses invokelatest and so does ImageIO. I don't see that being a problem in the profile results, but thought I'd check
  • For a 10x10 image, 50% of the time in load is spent figuring out the file format. That's probably ok for such a small image
Profiling

FileIO.save

julia> @profile for i in 1:10000
       FileIO.save("img.tiff", img)
       end

julia> Profile.print(mincount=3)
Overhead ╎ [+additional indent] Count File:Line; Function
=========================================================
   ╎1125 @Base/client.jl:492; _start()
   ╎ 1125 @Base/client.jl:309; exec_options(opts::Base.JLOptions)
   ╎  1125 @Base/client.jl:379; run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, colo...1125 @Base/essentials.jl:708; invokelatest
   ╎    1125 @Base/essentials.jl:710; #invokelatest#21125 @Base/client.jl:394; (::Base.var"#913#915"{Bool, Bool, Bool})(REPL::Module)
   ╎    ╎ 1125 ...64/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:305; run_repl(repl::REPL.AbstractREPL, consumer::Any)
   ╎    ╎  1125 ...64/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:317; run_repl(repl::REPL.AbstractREPL, consumer::Any; backend_on_current_task::Bool)
   ╎    ╎   1125 ...64/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:185; start_repl_backend(backend::REPL.REPLBackend, consumer::Any)
   ╎    ╎    1125 ...4/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:200; repl_backend_loop(backend::REPL.REPLBackend)
   ╎    ╎     1125 ...4/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:139; eval_user_input(ast::Any, backend::REPL.REPLBackend)
   ╎    ╎    ╎ 1125 @Base/boot.jl:369; eval
   ╎    ╎    ╎  1125 @ProfileView/src/ProfileView.jl:35; top-level scope
   ╎    ╎    ╎   1125 .../usr/share/julia/stdlib/v1.7/Profile/src/Profile.jl:28; macro expansion
  2╎    ╎    ╎    1125 REPL[6]:2; macro expansion
   ╎    ╎    ╎     1123 @FileIO/src/loadsave.jl:140; save(file::String, args::Matrix{Gray{N0f8}})
   ╎    ╎    ╎    ╎ 1123 @FileIO/src/loadsave.jl:140; #save#19
  3╎    ╎    ╎    ╎  1094 @FileIO/src/loadsave.jl:222; save
  1╎    ╎    ╎    ╎   8    @FileIO/src/loadsave.jl:224; save(q::Formatted, data::Any; options::Any)
   ╎    ╎    ╎    ╎    7    @Base/stat.jl:311; isdir(path::String)
  6╎    ╎    ╎    ╎     6    @Base/stat.jl:67; stat(path::String)
   ╎    ╎    ╎    ╎   13   @FileIO/src/loadsave.jl:225; save(q::Formatted, data::Any; options::Any)
   ╎    ╎    ╎    ╎    12   @Base/stat.jl:311; isdir
 10╎    ╎    ╎    ╎     10   @Base/stat.jl:67; stat(path::String)
   ╎    ╎    ╎    ╎   23   @FileIO/src/loadsave.jl:231; save(q::Formatted, data::Any; options::Any)
  2╎    ╎    ╎    ╎    23   @FileIO/src/loadsave.jl:20; checked_import
   ╎    ╎    ╎    ╎     14   @Base/lock.jl:187; lock(f::FileIO.var"#14#15"{Symbol}, l::ReentrantLock)
 14╎    ╎    ╎    ╎    ╎ 14   @FileIO/src/loadsave.jl:22; (::FileIO.var"#14#15"{Symbol})()
   ╎    ╎    ╎    ╎     3    @Base/lock.jl:189; lock(f::FileIO.var"#14#15"{Symbol}, l::ReentrantLock)
   ╎    ╎    ╎    ╎    ╎ 3    @Base/lock.jl:127; unlock(rl::ReentrantLock)
   ╎    ╎    ╎    ╎    ╎  3    @Base/condition.jl:73; lock
  4╎    ╎    ╎    ╎   1041 @FileIO/src/loadsave.jl:233; save(q::Formatted, data::Any; options::Any)
   ╎    ╎    ╎    ╎    1036 @Base/essentials.jl:708; invokelatest
  6╎    ╎    ╎    ╎     1036 @Base/essentials.jl:710; #invokelatest#2
  5╎    ╎    ╎    ╎    ╎ 1029 @ImageIO/src/ImageIO.jl:79; save(f::File{DataFormat{:TIFF}}, image::Matrix{Gray{N0f8}})
  3╎    ╎    ╎    ╎    ╎  3    @Base/Base.jl:26; getproperty(x::Module, f::Symbol)
   ╎    ╎    ╎    ╎    ╎  1018 @Base/essentials.jl:708; invokelatest
  2╎    ╎    ╎    ╎    ╎   1018 @Base/essentials.jl:710; #invokelatest#2
   ╎    ╎    ╎    ╎    ╎    1016 @TiffImages/src/types/dense.jl:118; save(filepath::String, data::Matrix{Gray{N0f8}})
  1╎    ╎    ╎    ╎    ╎     1016 @Base/io.jl:328; open
  3╎    ╎    ╎    ╎    ╎    ╎ 689  @Base/io.jl:328; open(::TiffImages.var"#3#4"{String, Matrix{Gray{N0f8}}}, ::String, ::V...
   ╎    ╎    ╎    ╎    ╎    ╎  686  @Base/iostream.jl:355; open(fname::String, mode::String)
   ╎    ╎    ╎    ╎    ╎    ╎   686  @Base/iostream.jl:355; open(fname::String, mode::String; lock::Bool)
  2╎    ╎    ╎    ╎    ╎    ╎    685  @Base/iostream.jl:282; open##kw
   ╎    ╎    ╎    ╎    ╎    ╎     5    @Base/iostream.jl:289; open(fname::String; lock::Bool, read::Nothing, write::Nothing, crea...
643╎    ╎    ╎    ╎    ╎    ╎     676  @Base/iostream.jl:293; open(fname::String; lock::Bool, read::Nothing, write::Nothing, crea...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 32   @Base/strings/io.jl:219; repr
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  32   @Base/strings/io.jl:219; #repr#414
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   32   @Base/strings/io.jl:101; sprint##kw
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    3    @Base/strings/io.jl:101; sprint(f::Function, args::String; context::Nothing, sizehint::Int64)
  1╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/iobuffer.jl:112; Type##kw
  7╎    ╎    ╎    ╎    ╎    ╎    ╎    27   @Base/strings/io.jl:105; sprint(f::Function, args::String; context::Nothing, sizehint::Int64)
  4╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @Base/io.jl:698; write(io::IOBuffer, c::Char)
  3╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/strings/io.jl:343; escape_string(io::IOBuffer, s::String, esc::Tuple{Char, Char}; k...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎     13   @Base/strings/io.jl:182; show(io::IOBuffer, s::String)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @Base/strings/io.jl:376; print_quoted
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @Base/char.jl:250; print
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @Base/io.jl:702; write(io::IOBuffer, c::Char)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    3    @Base/iobuffer.jl:438; write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/iobuffer.jl:330; ensureroom
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/array.jl:885; _growend!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 7    @Base/strings/io.jl:377; print_quoted
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  7    @Base/strings/io.jl:344; escape_string
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   7    @Base/strings/io.jl:351; escape_string(io::IOBuffer, s::String, esc::Tuple{Char, Char}; ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    3    @Base/char.jl:250; print
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/io.jl:702; write(io::IOBuffer, c::Char)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/iobuffer.jl:438; write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @Base/iobuffer.jl:330; ensureroom
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @Base/array.jl:885; _growend!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/strings/unicode.jl:489; isprint
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @Base/strings/unicode.jl:260; category_code(c::Char)
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/strings/unicode.jl:264; category_code
   ╎    ╎    ╎    ╎    ╎    ╎ 252  @Base/io.jl:330; open(::TiffImages.var"#3#4"{String, Matrix{Gray{N0f8}}}, ::String, ::V...
   ╎    ╎    ╎    ╎    ╎    ╎  252  @TiffImages/src/types/dense.jl:119; #3
   ╎    ╎    ╎    ╎    ╎    ╎   252  @TiffImages/src/types/dense.jl:116; save(io::Stream{DataFormat{:TIFF}, IOStream}, data::Matrix{Gray{N0f8}})
   ╎    ╎    ╎    ╎    ╎    ╎    13   @TiffImages/src/types/dense.jl:22; DenseTaggedImage
   ╎    ╎    ╎    ╎    ╎    ╎     7    @TiffImages/src/types/dense.jl:66; _constructifd(data::Matrix{Gray{N0f8}}, #unused#::Type{UInt32})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 7    @TiffImages/src/ifds.jl:23; IFD
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  7    @OrderedCollections/src/ordered_dict.jl:22; OrderedCollections.OrderedDict{UInt16, TiffImages.Tag}()
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @Base/array.jl:499; zeros
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    3    @Base/array.jl:503; zeros
   ╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/boot.jl:461; Array
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/boot.jl:452; Array
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @Base/boot.jl:471; Array
  4╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/boot.jl:452; Array
   ╎    ╎    ╎    ╎    ╎    ╎     4    @TiffImages/src/types/dense.jl:68; _constructifd(data::Matrix{Gray{N0f8}}, #unused#::Type{UInt32})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @TiffImages/src/ifds.jl:39; setindex!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @TiffImages/src/ifds.jl:41; setindex!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @TiffImages/src/ifds.jl:36; setindex!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @OrderedCollections/src/ordered_dict.jl:333; setindex!(h::OrderedCollections.OrderedDict{UInt16, TiffImages.Ta...
  3╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @OrderedCollections/src/ordered_dict.jl:302; _setindex!(h::OrderedCollections.OrderedDict{UInt16, TiffImages....
  1╎    ╎    ╎    ╎    ╎    ╎    239  @TiffImages/src/types/dense.jl:115; save
  1╎    ╎    ╎    ╎    ╎    ╎     7    @TiffImages/src/types/dense.jl:88; write(io::Stream{DataFormat{:TIFF}, IOStream}, img::TiffImages.Dens...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 6    @TiffImages/src/files.jl:39; write(file::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, I...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  6    @FileIO/src/types.jl:107; seekstart
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   6    @Base/iostream.jl:154; seekstart
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    5    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎     4    @TiffImages/src/types/dense.jl:94; write(io::Stream{DataFormat{:TIFF}, IOStream}, img::TiffImages.Dens...
   ╎    ╎    ╎    ╎    ╎    ╎     67   @TiffImages/src/types/dense.jl:99; write(io::Stream{DataFormat{:TIFF}, IOStream}, img::TiffImages.Dens...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 67   @FileIO/src/types.jl:106; seek
 67╎    ╎    ╎    ╎    ╎    ╎    ╎  67   @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎     10   @TiffImages/src/types/dense.jl:106; write(io::Stream{DataFormat{:TIFF}, IOStream}, img::TiffImages.Dens...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 9    @Base/strings/io.jl:174; string
  2╎    ╎    ╎    ╎    ╎    ╎    ╎  9    @Base/strings/io.jl:135; print_to_string(::Module, ::Vararg{Any})
  1╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @Base/strings/io.jl:35; print(io::IOBuffer, x::Module)
   ╎    ╎    ╎    ╎    ╎    ╎     7    @TiffImages/src/types/dense.jl:107; write(io::Stream{DataFormat{:TIFF}, IOStream}, img::TiffImages.Dens...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 7    @OrderedCollections/src/dict_sorting.jl:5; sort!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  7    @OrderedCollections/src/dict_sorting.jl:12; sort!(d::OrderedCollections.OrderedDict{UInt16, TiffImages.Tag}; by...
  2╎    ╎    ╎    ╎    ╎    ╎    ╎   7    @Base/sort.jl:935; sortperm
  4╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/sort.jl:952; sortperm(v::Vector{UInt16}; alg::Base.Sort.QuickSortAlg, lt::Func...
   ╎    ╎    ╎    ╎    ╎    ╎     13   @TiffImages/src/types/dense.jl:109; write(io::Stream{DataFormat{:TIFF}, IOStream}, img::TiffImages.Dens...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 13   @FileIO/src/types.jl:106; seek
 13╎    ╎    ╎    ╎    ╎    ╎    ╎  13   @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎     128  @TiffImages/src/types/dense.jl:110; write(io::Stream{DataFormat{:TIFF}, IOStream}, img::TiffImages.Dens...
  1╎    ╎    ╎    ╎    ╎    ╎    ╎ 27   @TiffImages/src/ifds.jl:212; write(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOS...
 13╎    ╎    ╎    ╎    ╎    ╎    ╎  14   @Base/pair.jl:50; indexed_iterate
  6╎    ╎    ╎    ╎    ╎    ╎    ╎  12   @TiffImages/src/ifds.jl:27; iterate
  5╎    ╎    ╎    ╎    ╎    ╎    ╎   5    @OrderedCollections/src/ordered_dict.jl:450; iterate(t::OrderedCollections.OrderedDict{UInt16, TiffImages.Tag})
  3╎    ╎    ╎    ╎    ╎    ╎    ╎ 15   @TiffImages/src/ifds.jl:214; write(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOS...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @TiffImages/src/tags.jl:159; write(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IO...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @TiffImages/src/files.jl:106; write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @TiffImages/src/tags.jl:161; write(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IO...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @FileIO/src/types.jl:114; write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    3    @Base/io.jl:652; write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/io.jl:649; write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/io.jl:646; unsafe_write(s::IOStream, p::Base.RefValue{UInt16}, n::Int64)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @Base/io.jl:648; unsafe_write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 61   @TiffImages/src/ifds.jl:215; write(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOS...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @OrderedCollections/src/ordered_dict.jl:333; setindex!(h::OrderedCollections.OrderedDict{TiffImages.Tag, Vector...
 46╎    ╎    ╎    ╎    ╎    ╎    ╎  56   @TiffImages/src/ifds.jl:28; iterate
  8╎    ╎    ╎    ╎    ╎    ╎    ╎   8    @OrderedCollections/src/ordered_dict.jl:454; iterate(t::OrderedCollections.OrderedDict{UInt16, TiffImages.Tag},...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 9    @TiffImages/src/ifds.jl:244; write(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOS...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  9    @TiffImages/src/files.jl:109; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   9    @FileIO/src/types.jl:106; seek
  9╎    ╎    ╎    ╎    ╎    ╎    ╎    9    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎ 6    @TiffImages/src/ifds.jl:248; write(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOS...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  6    @TiffImages/src/files.jl:109; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   6    @FileIO/src/types.jl:106; seek
  6╎    ╎    ╎    ╎    ╎    ╎    ╎    6    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎ 73   @Base/io.jl:332; open(::TiffImages.var"#3#4"{String, Matrix{Gray{N0f8}}}, ::String, ::V...
 73╎    ╎    ╎    ╎    ╎    ╎  73   @Base/iostream.jl:43; close
   ╎    ╎    ╎    ╎    ╎  3    @ImageIO/src/ImageIO.jl:8; checked_import
   ╎    ╎    ╎    ╎    ╎   3    @Base/lock.jl:187; lock(f::ImageIO.var"#1#2"{Symbol}, l::ReentrantLock)
  1╎    ╎    ╎    ╎  29   @FileIO/src/query.jl:73; query##kw
   ╎    ╎    ╎    ╎   9    @FileIO/src/query.jl:73; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    9    @Base/stat.jl:311; isfile
  7╎    ╎    ╎    ╎     7    @Base/stat.jl:67; stat(path::String)
   ╎    ╎    ╎    ╎   5    @FileIO/src/query.jl:74; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    3    @Base/path.jl:206; splitext(path::String)
   ╎    ╎    ╎    ╎     3    @Base/regex.jl:325; match
   ╎    ╎    ╎    ╎    ╎ 3    @Base/regex.jl:306; match
   ╎    ╎    ╎    ╎   5    @FileIO/src/query.jl:77; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    5    @FileIO/src/query.jl:96; hasmagic
   ╎    ╎    ╎    ╎     5    @Base/reducedim.jl:895; any
   ╎    ╎    ╎    ╎    ╎ 5    @Base/reducedim.jl:895; #any#731
   ╎    ╎    ╎    ╎    ╎  5    @Base/reduce.jl:1095; _any(f::typeof(FileIO.hasmagic), itr::Vector{Symbol}, #unused#::Colon)
  4╎    ╎    ╎    ╎    ╎   5    @FileIO/src/query.jl:95; hasmagic(s::Symbol)
  9╎    ╎    ╎    ╎   9    @FileIO/src/query.jl:81; query(filename::String; checkfile::Bool)
Total snapshots: 1137

FileIO.load

julia> @profile for i in 1:10000
       FileIO.load("img.tiff")
       end

julia> Profile.print(mincount=3)
Overhead ╎ [+additional indent] Count File:Line; Function
=========================================================
   ╎1427 @Base/client.jl:492; _start()
   ╎ 1427 @Base/client.jl:309; exec_options(opts::Base.JLOptions)
   ╎  1427 @Base/client.jl:379; run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, colo...1427 @Base/essentials.jl:708; invokelatest
   ╎    1427 @Base/essentials.jl:710; #invokelatest#21427 @Base/client.jl:394; (::Base.var"#913#915"{Bool, Bool, Bool})(REPL::Module)
   ╎    ╎ 1427 ...64/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:305; run_repl(repl::REPL.AbstractREPL, consumer::Any)
   ╎    ╎  1427 ...64/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:317; run_repl(repl::REPL.AbstractREPL, consumer::Any; backend_on_current_task::Bool)
   ╎    ╎   1427 ...64/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:185; start_repl_backend(backend::REPL.REPLBackend, consumer::Any)
   ╎    ╎    1427 ...4/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:200; repl_backend_loop(backend::REPL.REPLBackend)
   ╎    ╎     1427 ...4/build/usr/share/julia/stdlib/v1.7/REPL/src/REPL.jl:139; eval_user_input(ast::Any, backend::REPL.REPLBackend)
   ╎    ╎    ╎ 1427 @Base/boot.jl:369; eval
   ╎    ╎    ╎  1427 @ProfileView/src/ProfileView.jl:35; top-level scope
   ╎    ╎    ╎   1427 .../usr/share/julia/stdlib/v1.7/Profile/src/Profile.jl:28; macro expansion
   ╎    ╎    ╎    1427 REPL[11]:2; macro expansion
   ╎    ╎    ╎     1427 @FileIO/src/loadsave.jl:137; load
   ╎    ╎    ╎    ╎ 1427 @FileIO/src/loadsave.jl:137; #load#16
   ╎    ╎    ╎    ╎  688  @FileIO/src/loadsave.jl:189; load
   ╎    ╎    ╎    ╎   688  @FileIO/src/loadsave.jl:189; #load#30
   ╎    ╎    ╎    ╎    688  @Base/essentials.jl:708; invokelatest
   ╎    ╎    ╎    ╎     688  @Base/essentials.jl:710; #invokelatest#2
  1╎    ╎    ╎    ╎    ╎ 688  @FileIO/src/loadsave.jl:192; load_filename(::Formatted, ::String)
   ╎    ╎    ╎    ╎    ╎  29   @FileIO/src/loadsave.jl:197; load_filename(::Formatted, ::String; options::Any)
   ╎    ╎    ╎    ╎    ╎   29   @Base/stat.jl:311; isfile
 27╎    ╎    ╎    ╎    ╎    27   @Base/stat.jl:67; stat(path::String)
   ╎    ╎    ╎    ╎    ╎  12   @FileIO/src/loadsave.jl:203; load_filename(::Formatted, ::String; options::Any)
   ╎    ╎    ╎    ╎    ╎   12   @FileIO/src/loadsave.jl:20; checked_import
   ╎    ╎    ╎    ╎    ╎    11   @Base/lock.jl:187; lock(f::FileIO.var"#14#15"{Symbol}, l::ReentrantLock)
  8╎    ╎    ╎    ╎    ╎     8    @FileIO/src/loadsave.jl:22; (::FileIO.var"#14#15"{Symbol})()
  1╎    ╎    ╎    ╎    ╎  643  @FileIO/src/loadsave.jl:205; load_filename(::Formatted, ::String; options::Any)
   ╎    ╎    ╎    ╎    ╎   642  @Base/essentials.jl:708; invokelatest
  2╎    ╎    ╎    ╎    ╎    642  @Base/essentials.jl:710; #invokelatest#2
  1╎    ╎    ╎    ╎    ╎     640  @ImageIO/src/ImageIO.jl:72; load(f::File{DataFormat{:TIFF}})
  4╎    ╎    ╎    ╎    ╎    ╎ 639  @ImageIO/src/ImageIO.jl:72; #load#19
  7╎    ╎    ╎    ╎    ╎    ╎  632  @Base/essentials.jl:708; invokelatest(::Any, ::Any, ::Vararg{Any})
  1╎    ╎    ╎    ╎    ╎    ╎   624  @Base/essentials.jl:710; invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::Base.Iterators.Pair...
   ╎    ╎    ╎    ╎    ╎    ╎    622  @TiffImages/src/load.jl:2; load(filepath::String)
   ╎    ╎    ╎    ╎    ╎    ╎     622  @TiffImages/src/load.jl:2; #load#5
  1╎    ╎    ╎    ╎    ╎    ╎    ╎ 622  @Base/io.jl:328; open
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  43   @Base/io.jl:328; open(f::TiffImages.var"#6#7"{Bool, Bool}, args::String; kwargs::Ba...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   43   @Base/iostream.jl:282; open
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/iostream.jl:289; open(fname::String; lock::Bool, read::Nothing, write::Nothing, cr...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/iostream.jl:32; IOStream
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/iostream.jl:26; IOStream(name::String, finalize::Bool)
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @Base/iostream.jl:21; IOStream
 23╎    ╎    ╎    ╎    ╎    ╎    ╎    39   @Base/iostream.jl:293; open(fname::String; lock::Bool, read::Nothing, write::Nothing, cr...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎     16   @Base/strings/io.jl:219; repr
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 16   @Base/strings/io.jl:219; #repr#414
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  16   @Base/strings/io.jl:101; sprint##kw
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   15   @Base/strings/io.jl:105; sprint(f::Function, args::String; context::Nothing, sizehint::I...
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    12   @Base/strings/io.jl:182; show(io::IOBuffer, s::String)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     10   @Base/strings/io.jl:377; print_quoted
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 10   @Base/strings/io.jl:344; escape_string
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  7    @Base/strings/io.jl:351; escape_string(io::IOBuffer, s::String, esc::Tuple{Char, Char...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   5    @Base/char.jl:250; print
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/io.jl:702; write(io::IOBuffer, c::Char)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @Base/iobuffer.jl:438; write
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/iobuffer.jl:330; ensureroom
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @Base/array.jl:885; _growend!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎  577  @Base/io.jl:330; open(f::TiffImages.var"#6#7"{Bool, Bool}, args::String; kwargs::Ba...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎   577  @TiffImages/src/load.jl:3; #6
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    577  @TiffImages/src/load.jl:7; load##kw
  3╎    ╎    ╎    ╎    ╎    ╎    ╎     577  @TiffImages/src/load.jl:7; load(io::IOStream; verbose::Bool, mmap::Bool)
  2╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 54   @TiffImages/src/files.jl:36; read
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  8    @TiffImages/src/files.jl:27; read(io::Stream{DataFormat{:TIFF}, IOStream}, #unused#::Type{Tif...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   8    @FileIO/src/types.jl:107; seekstart
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    8    @Base/iostream.jl:154; seekstart
  8╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     8    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  25   @TiffImages/src/files.jl:29; read(io::Stream{DataFormat{:TIFF}, IOStream}, #unused#::Type{Tif...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   12   @TiffImages/src/utils.jl:50; need_bswap(io::Stream{DataFormat{:TIFF}, IOStream})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    12   @FileIO/src/types.jl:107; seekstart
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     12   @Base/iostream.jl:154; seekstart
 12╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 12   @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   13   @TiffImages/src/utils.jl:51; need_bswap(io::Stream{DataFormat{:TIFF}, IOStream})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    13   @FileIO/src/types.jl:120; read
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     13   @Base/iostream.jl:560; read
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 13   @Base/iostream.jl:561; read(s::IOStream, nb::Int64; all::Bool)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  13   @Base/iostream.jl:513; readbytes!##kw
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   13   @Base/iostream.jl:513; #readbytes!#626
 13╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    13   @Base/iostream.jl:465; readbytes_all!(s::IOStream, b::Vector{UInt8}, nb::Int64)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  9    @TiffImages/src/files.jl:30; read(io::Stream{DataFormat{:TIFF}, IOStream}, #unused#::Type{Tif...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   5    @TiffImages/src/utils.jl:58; offsetsize(io::Stream{DataFormat{:TIFF}, IOStream})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    5    @FileIO/src/types.jl:106; seek
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     5    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @TiffImages/src/utils.jl:60; offsetsize(io::Stream{DataFormat{:TIFF}, IOStream})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @FileIO/src/types.jl:113; read!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @Base/io.jl:736; read!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @Base/io.jl:724; unsafe_read
  4╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @Base/iostream.jl:43; unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt64)
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  5    @TiffImages/src/files.jl:33; read(io::Stream{DataFormat{:TIFF}, IOStream}, #unused#::Type{Tif...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @TiffImages/src/utils.jl:33; extract_filename(io::IOStream)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @Base/regex.jl:325; match
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/regex.jl:306; match
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 520  @TiffImages/src/load.jl:9; (::TiffImages.var"#load##kw")(::NamedTuple{(:verbose, :mmap), Tu...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  107  @TiffImages/src/load.jl:15; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, I...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   7    @TiffImages/src/ifds.jl:74; iterate
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    7    @FileIO/src/types.jl:106; seek
  7╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     7    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   100  @TiffImages/src/ifds.jl:75; iterate
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    9    @TiffImages/src/ifds.jl:60; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF},...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     9    @TiffImages/src/files.jl:73; read
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 9    @FileIO/src/types.jl:120; read
  9╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  9    @Base/iostream.jl:408; read(s::IOStream, T::Type{UInt16})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    73   @TiffImages/src/ifds.jl:65; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF},...
 15╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     15   @TiffImages/src/tags.jl:0; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     3    @TiffImages/src/tags.jl:86; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @TiffImages/src/files.jl:73; read
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    @FileIO/src/types.jl:120; read
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @TiffImages/src/tags.jl:87; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}...
  4╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @Base/boot.jl:452; Array
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @TiffImages/src/tags.jl:88; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @TiffImages/src/files.jl:83; read!(file::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TI...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @Base/io.jl:736; read!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @Base/io.jl:724; unsafe_read
 19╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     21   @TiffImages/src/tags.jl:95; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}...
 20╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     20   @TiffImages/src/tags.jl:105; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}...
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    16   @TiffImages/src/ifds.jl:66; read(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF},...
  7╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     7    @Base/Base.jl:33; getproperty(x::TiffImages.Tag{UInt16}, f::Symbol)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @OrderedCollections/src/ordered_dict.jl:333; setindex!(h::OrderedCollections.OrderedDict{UInt16, TiffImage...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  66   @TiffImages/src/load.jl:16; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, I...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   12   @TiffImages/src/ifds.jl:46; load!(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF},...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    10   @Base/abstractdict.jl:64; iterate
 10╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     10   @OrderedCollections/src/ordered_dict.jl:450; iterate(t::OrderedCollections.OrderedDict{UInt16, TiffImages....
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   54   @TiffImages/src/ifds.jl:47; load!(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF},...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    16   @Base/abstractdict.jl:64; iterate
 16╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     16   @OrderedCollections/src/ordered_dict.jl:454; iterate(t::OrderedCollections.OrderedDict{UInt16, TiffImages....
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/abstractdict.jl:66; iterate
  4╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @Base/tuple.jl:29; getindex
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    5    @TiffImages/src/tags.jl:48; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    7    @TiffImages/src/tags.jl:51; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     7    @FileIO/src/types.jl:105; position
  7╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 7    @Base/iostream.jl:43; position
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    6    @TiffImages/src/tags.jl:53; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     6    @TiffImages/src/files.jl:83; read!(file::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIF...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 6    @Base/io.jl:736; read!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  6    @Base/io.jl:724; unsafe_read
  6╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   6    @Base/iostream.jl:43; unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt64)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @TiffImages/src/tags.jl:72; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @TiffImages/src/files.jl:109; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @FileIO/src/types.jl:106; seek
  4╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @Base/iostream.jl:43; seek
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  150  @TiffImages/src/load.jl:19; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, I...
  2╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @TiffImages/src/ifds.jl:113; output(ifd::TiffImages.IFD{UInt32})
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   120  @TiffImages/src/ifds.jl:119; output(ifd::TiffImages.IFD{UInt32})
113╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    119  @Base/Base.jl:33; getproperty(x::TiffImages.Tag{UInt16}, f::Symbol)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @Base/compiler/typeinfer.jl:926; typeinf_ext_toplevel(mi::Core.MethodInstance, world::UInt64)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @Base/compiler/typeinfer.jl:930; typeinf_ext_toplevel(interp::Core.Compiler.NativeInterpreter,...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @Base/compiler/typeinfer.jl:897; typeinf_ext(interp::Core.Compiler.NativeInterpreter, mi::Cor...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @Base/compiler/typeinfer.jl:209; typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    @Base/compiler/typeinfer.jl:214; _typeinf(interp::Core.Compiler.NativeInterpreter, frame::Co...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    @Base/compiler/abstractinterpretation.jl:1580; typeinf_nocycle(interp::Core.Compiler.NativeInterpreter, f...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @Base/compiler/abstractinterpretation.jl:1520; typeinf_local(interp::Core.Compiler.NativeInterpreter, fr...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  4    @Base/compiler/abstractinterpretation.jl:1219; abstract_eval_statement(interp::Core.Compiler.NativeInter...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    ...e/compiler/abstractinterpretation.jl:1079; abstract_call(interp::Core.Compiler.NativeInterpreter, f...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    4    ...e/compiler/abstractinterpretation.jl:1099; abstract_call(interp::Core.Compiler.NativeInterpreter, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     4    .../compiler/abstractinterpretation.jl:1058; abstract_call_known(interp::Core.Compiler.NativeInterpr...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    .../compiler/abstractinterpretation.jl:142; abstract_call_gf_by_type(interp::Core.Compiler.NativeIn...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  3    .../compiler/abstractinterpretation.jl:516; abstract_call_method(interp::Core.Compiler.NativeInter...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @Base/compiler/typeinfer.jl:811; typeinf_edge(interp::Core.Compiler.NativeInterpreter, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    3    @Base/compiler/typeinfer.jl:209; typeinf(interp::Core.Compiler.NativeInterpreter, fram...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   6    @TiffImages/src/ifds.jl:127; output(ifd::TiffImages.IFD{UInt32})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    6    @Base/idset.jl:7; IdSet
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     6    @Base/iddict.jl:30; IdDict
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 5    @Base/boot.jl:452; Array
  6╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   6    @TiffImages/src/ifds.jl:130; output(ifd::TiffImages.IFD{UInt32})
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   4    @TiffImages/src/ifds.jl:146; output(ifd::TiffImages.IFD{UInt32})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  169  @TiffImages/src/load.jl:36; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, I...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   169  @TiffImages/src/load.jl:61; load##kw
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    106  @TiffImages/src/load.jl:62; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     9    @TiffImages/src/layout.jl:80; getcache(ifd::TiffImages.IFD{UInt32})
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 7    @TiffImages/src/layout.jl:71; rawtype(ifd::TiffImages.IFD{UInt32})
  6╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     63   @TiffImages/src/layout.jl:84; getcache(ifd::TiffImages.IFD{UInt32})
  2╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/tuple.jl:86; indexed_iterate(t::Tuple{UnionAll, Bool}, i::Int64)
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 4    @TiffImages/src/layout.jl:13; interpretation(ifd::TiffImages.IFD{UInt32})
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 50   @TiffImages/src/layout.jl:22; interpretation(ifd::TiffImages.IFD{UInt32})
 34╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  45   @TiffImages/src/layout.jl:39; interpretation(p::TiffImages.PhotometricInterpretations, extr...
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   3    @Base/boot.jl:251; TypeVar(n::Symbol, ub::Any)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   6    @TiffImages/src/layout.jl:31; interpretation(p::TiffImages.PhotometricInterpretations)
  6╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    6    @Base/essentials.jl:695; Val(x::TiffImages.PhotometricInterpretations)
 27╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     33   @TiffImages/src/layout.jl:85; getcache(ifd::TiffImages.IFD{UInt32})
  5╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 5    @TiffImages/src/layout.jl:2; ncols(ifd::TiffImages.IFD{UInt32})
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    63   @TiffImages/src/load.jl:63; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, ...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     28   @TiffImages/src/ifds.jl:167; read!(target::Matrix{Gray{N0f8}}, tf::TiffImages.TiffFile{UIn...
  3╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @TiffImages/src/ifds.jl:116; output(ifd::TiffImages.IFD{UInt32})
  6╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 6    @TiffImages/src/ifds.jl:125; output(ifd::TiffImages.IFD{UInt32})
  2╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 3    @TiffImages/src/ifds.jl:146; output(ifd::TiffImages.IFD{UInt32})
  4╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     18   @TiffImages/src/ifds.jl:198; read!(target::Matrix{Gray{N0f8}}, tf::TiffImages.TiffFile{UIn...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 14   @TiffImages/src/files.jl:109; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  14   @FileIO/src/types.jl:106; seek
 13╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   13   @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     15   @TiffImages/src/ifds.jl:199; read!(target::Matrix{Gray{N0f8}}, tf::TiffImages.TiffFile{UIn...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 15   @TiffImages/src/compression.jl:8; read!(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}...
  1╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  15   @TiffImages/src/compression.jl:10; read!(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   14   @TiffImages/src/files.jl:83; read!(file::TiffImages.TiffFile{UInt32, Stream{DataFormat{:T...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    14   @Base/io.jl:742; read!
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎     14   @Base/io.jl:724; unsafe_read
 14╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎ 14   @Base/iostream.jl:43; unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt64)
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎  27   @TiffImages/src/load.jl:56; load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, I...
   ╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎   27   @FileIO/src/types.jl:103; close
 27╎    ╎    ╎    ╎    ╎    ╎    ╎    ╎    27   @Base/iostream.jl:43; close
   ╎    ╎    ╎    ╎    ╎    ╎  3    @ImageIO/src/ImageIO.jl:8; checked_import
  1╎    ╎    ╎    ╎  739  @FileIO/src/query.jl:73; query
   ╎    ╎    ╎    ╎   18   @FileIO/src/query.jl:73; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    18   @Base/stat.jl:311; isfile
 17╎    ╎    ╎    ╎     17   @Base/stat.jl:67; stat(path::String)
   ╎    ╎    ╎    ╎   10   @FileIO/src/query.jl:74; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    8    @Base/path.jl:206; splitext(path::String)
   ╎    ╎    ╎    ╎     8    @Base/regex.jl:325; match
  1╎    ╎    ╎    ╎    ╎ 8    @Base/regex.jl:306; match
   ╎    ╎    ╎    ╎    ╎  6    @Base/regex.jl:316; match(re::Regex, str::String, idx::Int64, add_opts::UInt32)
   ╎    ╎    ╎    ╎    ╎   5    @Base/array.jl:671; _array_for
   ╎    ╎    ╎    ╎    ╎    5    @Base/abstractarray.jl:784; similar
   ╎    ╎    ╎    ╎    ╎     5    @Base/abstractarray.jl:785; similar
   ╎    ╎    ╎    ╎    ╎    ╎ 5    @Base/boot.jl:461; Array
  5╎    ╎    ╎    ╎    ╎    ╎  5    @Base/boot.jl:452; Array
  1╎    ╎    ╎    ╎   6    @FileIO/src/query.jl:75; query(filename::String; checkfile::Bool)
  1╎    ╎    ╎    ╎    5    @Base/dict.jl:550; haskey
   ╎    ╎    ╎    ╎   18   @FileIO/src/query.jl:77; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    18   @FileIO/src/query.jl:96; hasmagic
   ╎    ╎    ╎    ╎     18   @Base/reducedim.jl:895; any
   ╎    ╎    ╎    ╎    ╎ 18   @Base/reducedim.jl:895; #any#731
   ╎    ╎    ╎    ╎    ╎  17   @Base/reduce.jl:1095; _any(f::typeof(FileIO.hasmagic), itr::Vector{Symbol}, #unused#::Colon)
 16╎    ╎    ╎    ╎    ╎   17   @FileIO/src/query.jl:95; hasmagic(s::Symbol)
  1╎    ╎    ╎    ╎   10   @FileIO/src/query.jl:83; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    9    @FileIO/src/query.jl:102; hasfunction
   ╎    ╎    ╎    ╎     9    @Base/reducedim.jl:895; any
   ╎    ╎    ╎    ╎    ╎ 9    @Base/reducedim.jl:895; #any#731
   ╎    ╎    ╎    ╎    ╎  9    @Base/reduce.jl:1095; _any(f::typeof(FileIO.hasfunction), itr::Vector{Symbol}, #unused#::Colon)
  9╎    ╎    ╎    ╎    ╎   9    @FileIO/src/query.jl:101; hasfunction(s::Symbol)
  2╎    ╎    ╎    ╎   673  @FileIO/src/query.jl:89; query(filename::String; checkfile::Bool)
   ╎    ╎    ╎    ╎    37   @Base/iostream.jl:282; open
 28╎    ╎    ╎    ╎     35   @Base/iostream.jl:293; open(fname::String; lock::Bool, read::Nothing, write::Nothing, create::No...
   ╎    ╎    ╎    ╎    ╎ 6    @Base/strings/io.jl:219; repr
   ╎    ╎    ╎    ╎    ╎  6    @Base/strings/io.jl:219; #repr#414
   ╎    ╎    ╎    ╎    ╎   6    @Base/strings/io.jl:101; sprint##kw
   ╎    ╎    ╎    ╎    ╎    6    @Base/strings/io.jl:105; sprint(f::Function, args::String; context::Nothing, sizehint::Int64)
   ╎    ╎    ╎    ╎    ╎     6    @Base/strings/io.jl:182; show(io::IOBuffer, s::String)
   ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/strings/io.jl:376; print_quoted
  1╎    ╎    ╎    ╎    ╎    ╎  3    @Base/char.jl:250; print
   ╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/strings/io.jl:377; print_quoted
   ╎    ╎    ╎    ╎    ╎    ╎  3    @Base/strings/io.jl:344; escape_string
   ╎    ╎    ╎    ╎    67   @Base/path.jl:393; abspath
   ╎    ╎    ╎    ╎     3    @Base/file.jl:49; pwd()
   ╎    ╎    ╎    ╎    ╎ 3    @Base/iobuffer.jl:31; StringVector
  3╎    ╎    ╎    ╎    ╎  3    @Base/strings/string.jl:74; _string_n
 13╎    ╎    ╎    ╎     13   @Base/file.jl:52; pwd()
   ╎    ╎    ╎    ╎     13   @Base/path.jl:349; normpath(path::String)
   ╎    ╎    ╎    ╎    ╎ 13   @Base/strings/util.jl:403; split
   ╎    ╎    ╎    ╎    ╎  13   @Base/strings/util.jl:403; #split#401
   ╎    ╎    ╎    ╎    ╎   6    @Base/strings/util.jl:425; _split(str::String, splitter::Regex, limit::Int64, keepempty::Bool, strs...
   ╎    ╎    ╎    ╎    ╎    5    @Base/array.jl:930; push!
  5╎    ╎    ╎    ╎    ╎     5    @Base/array.jl:885; _growend!
   ╎    ╎    ╎    ╎    ╎   4    @Base/strings/util.jl:430; _split(str::String, splitter::Regex, limit::Int64, keepempty::Bool, strs...
   ╎    ╎    ╎    ╎    ╎    4    @Base/regex.jl:330; findnext
   ╎    ╎    ╎    ╎     4    @Base/path.jl:350; normpath(path::String)
   ╎    ╎    ╎    ╎    ╎ 4    @Base/array.jl:2433; filter!(f::Base.Filesystem.var"#2#3", a::Vector{SubString{String}})
  4╎    ╎    ╎    ╎    ╎  4    @Base/array.jl:1136; sizehint!
   ╎    ╎    ╎    ╎     30   @Base/path.jl:369; normpath(path::String)
   ╎    ╎    ╎    ╎    ╎ 30   @Base/strings/io.jl:293; join
   ╎    ╎    ╎    ╎    ╎  30   @Base/strings/io.jl:101; sprint
  3╎    ╎    ╎    ╎    ╎   3    @Base/strings/io.jl:100; sprint(::Function, ::Vector{SubString{String}}, ::Vararg{Any}; context::...
  4╎    ╎    ╎    ╎    ╎   25   @Base/strings/io.jl:105; sprint(::Function, ::Vector{SubString{String}}, ::Vararg{Any}; context::...
   ╎    ╎    ╎    ╎    ╎    17   @Base/strings/io.jl:287; join(io::IOBuffer, strings::Vector{SubString{String}}, delim::String)
   ╎    ╎    ╎    ╎    ╎     17   @Base/strings/io.jl:187; print
   ╎    ╎    ╎    ╎    ╎    ╎ 17   @Base/strings/io.jl:185; write
   ╎    ╎    ╎    ╎    ╎    ╎  17   @Base/iobuffer.jl:419; unsafe_write(to::IOBuffer, p::Ptr{UInt8}, nb::UInt64)
   ╎    ╎    ╎    ╎    ╎    ╎   17   @Base/iobuffer.jl:330; ensureroom
 15╎    ╎    ╎    ╎    ╎    ╎    17   @Base/array.jl:885; _growend!
   ╎    ╎    ╎    ╎    ╎    3    @Base/strings/io.jl:288; join(io::IOBuffer, strings::Vector{SubString{String}}, delim::String)
   ╎    ╎    ╎    ╎    ╎     3    @Base/strings/io.jl:187; print
  1╎    ╎    ╎    ╎    ╎    ╎ 3    @Base/strings/io.jl:185; write
   ╎    ╎    ╎    ╎    17   @FileIO/src/query.jl:114; query(io::IOStream, filename::String)
   ╎    ╎    ╎    ╎     17   @Base/pair.jl:66; first
 17╎    ╎    ╎    ╎    ╎ 17   @Base/Base.jl:33; getproperty
118╎    ╎    ╎    ╎    122  @FileIO/src/query.jl:115; query(io::IOStream, filename::String)
  3╎    ╎    ╎    ╎     3    @Base/tuple.jl:24; length(t::Tuple)
208╎    ╎    ╎    ╎    212  @FileIO/src/query.jl:116; query(io::IOStream, filename::String)
  4╎    ╎    ╎    ╎     4    @Base/tuple.jl:24; length(t::Tuple)
   ╎    ╎    ╎    ╎    34   @FileIO/src/query.jl:117; query(io::IOStream, filename::String)
   ╎    ╎    ╎    ╎     9    @Base/iostream.jl:42; eof
   ╎    ╎    ╎    ╎    ╎ 5    @Base/lock.jl:91; lock(rl::ReentrantLock)
   ╎    ╎    ╎    ╎    ╎  5    @Base/condition.jl:73; lock
   ╎    ╎    ╎    ╎    ╎   4    @Base/locks-mt.jl:66; lock(l::Base.Threads.SpinLock)
  4╎    ╎    ╎    ╎    ╎    4    @Base/promotion.jl:409; ==
   ╎    ╎    ╎    ╎     16   @Base/iostream.jl:43; eof
 16╎    ╎    ╎    ╎    ╎ 16   @Base/iostream.jl:231; _eof_nolock
   ╎    ╎    ╎    ╎     9    @Base/iostream.jl:44; eof
   ╎    ╎    ╎    ╎    ╎ 5    @Base/lock.jl:127; unlock(rl::ReentrantLock)
  1╎    ╎    ╎    ╎    ╎  5    @Base/condition.jl:73; lock
   ╎    ╎    ╎    ╎    ╎   3    @Base/locks-mt.jl:66; lock(l::Base.Threads.SpinLock)
  3╎    ╎    ╎    ╎    ╎    3    @Base/promotion.jl:409; ==
   ╎    ╎    ╎    ╎    8    @FileIO/src/query.jl:121; query(io::IOStream, filename::String)
   ╎    ╎    ╎    ╎     3    @Base/iostream.jl:42; read(s::IOStream, #unused#::Type{UInt8})
   ╎    ╎    ╎    ╎     4    @Base/iostream.jl:44; read(s::IOStream, #unused#::Type{UInt8})
   ╎    ╎    ╎    ╎    ╎ 4    @Base/lock.jl:127; unlock(rl::ReentrantLock)
  1╎    ╎    ╎    ╎    ╎  4    @Base/condition.jl:73; lock
 39╎    ╎    ╎    ╎    40   @FileIO/src/query.jl:123; query(io::IOStream, filename::String)
   ╎    ╎    ╎    ╎    28   @FileIO/src/query.jl:130; query(io::IOStream, filename::String)
 28╎    ╎    ╎    ╎     28   @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    71   @FileIO/src/query.jl:133; query(io::IOStream, filename::String)
   ╎    ╎    ╎    ╎     7    @FileIO/src/registry.jl:188; detect_bedgraph(io::IOStream)
   ╎    ╎    ╎    ╎    ╎ 7    @Base/iostream.jl:43; eof
  7╎    ╎    ╎    ╎    ╎  7    @Base/iostream.jl:231; _eof_nolock
  1╎    ╎    ╎    ╎     21   @FileIO/src/registry.jl:259; detect_noometiff(io::IOStream)
   ╎    ╎    ╎    ╎    ╎ 9    @FileIO/src/registry.jl:253; detecttiff(io::IOStream)
   ╎    ╎    ╎    ╎    ╎  9    @Base/iostream.jl:154; seekstart
  9╎    ╎    ╎    ╎    ╎   9    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    ╎ 11   @FileIO/src/registry.jl:254; detecttiff(io::IOStream)
   ╎    ╎    ╎    ╎    ╎  9    @Base/io.jl:736; read!
   ╎    ╎    ╎    ╎    ╎   9    @Base/io.jl:724; unsafe_read
  9╎    ╎    ╎    ╎    ╎    9    @Base/iostream.jl:43; unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt64)
   ╎    ╎    ╎    ╎     5    @FileIO/src/registry.jl:14; detect_rdata(io::IOStream)
   ╎    ╎    ╎    ╎    ╎ 5    @Base/iostream.jl:154; seekstart
  5╎    ╎    ╎    ╎    ╎  5    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎     6    @FileIO/src/registry.jl:15; detect_rdata(io::IOStream)
  6╎    ╎    ╎    ╎    ╎ 6    @Base/iostream.jl:43; read(s::IOStream, #unused#::Type{UInt8})
   ╎    ╎    ╎    ╎     8    @FileIO/src/registry.jl:25; detect_rdata_single(io::IOStream)
   ╎    ╎    ╎    ╎    ╎ 8    @Base/iostream.jl:154; seekstart
  8╎    ╎    ╎    ╎    ╎  8    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎     6    @FileIO/src/registry.jl:26; detect_rdata_single(io::IOStream)
  6╎    ╎    ╎    ╎    ╎ 6    @Base/iostream.jl:43; read(s::IOStream, #unused#::Type{UInt8})
   ╎    ╎    ╎    ╎     7    @FileIO/src/registry.jl:28; detect_rdata_single(io::IOStream)
   ╎    ╎    ╎    ╎    ╎ 7    @Base/iostream.jl:154; seekstart
  7╎    ╎    ╎    ╎    ╎  7    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎     5    @FileIO/src/registry.jl:153; detectwav(io::IOStream)
   ╎    ╎    ╎    ╎    ╎ 5    @Base/iostream.jl:154; seekstart
  5╎    ╎    ╎    ╎    ╎  5    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎     6    @FileIO/src/registry.jl:154; detectwav(io::IOStream)
   ╎    ╎    ╎    ╎    ╎ 6    @Base/io.jl:736; read!
   ╎    ╎    ╎    ╎    ╎  6    @Base/io.jl:724; unsafe_read
  6╎    ╎    ╎    ╎    ╎   6    @Base/iostream.jl:43; unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt64)
  3╎    ╎    ╎    ╎    12   @FileIO/src/query.jl:134; query(io::IOStream, filename::String)
  8╎    ╎    ╎    ╎     8    @Base/iostream.jl:43; seek
   ╎    ╎    ╎    ╎    17   @FileIO/src/types.jl:94; file!(strm::Stream{DataFormat{:TIFF}, IOStream})
 17╎    ╎    ╎    ╎     17   @Base/iostream.jl:43; close
Total snapshots: 1453

@timholy
Copy link
Member

timholy commented Feb 26, 2021

In the steps below, focus on the "exclusive time" (first number) reported for ROOT, which you probably already know is everything except inference (see the paragraph starting "The ROOT node is a bit different" if you're not familiar with this).

Prior to my inference improvements (and without adding any precompile directives):

julia> using SnoopCompile, TiffImages
[ Info: Precompiling TiffImages [731e570b-9d59-4bfa-96dc-6df516fadf69]

julia> using Colors, FixedPointNumbers

julia> img = rand(RGB{N0f8}, 100, 100);

julia> tinf = @snoopi_deep begin
           TiffImages.save("/tmp/file.tiff", img)
           imgs = TiffImages.load("/tmp/file.tiff")
       end
InferenceTimingNode: 1.874157/6.022248 on InferenceFrameInfo for Core.Compiler.Timings.ROOT() with 74 direct children

vs post-inference-improvement:

julia> tinf = @snoopi_deep begin
           TiffImages.save("/tmp/file.tiff", img)
           imgs = TiffImages.load("/tmp/file.tiff")
       end
InferenceTimingNode: 2.911983/8.437081 on InferenceFrameInfo for Core.Compiler.Timings.ROOT() with 53 direct children

The vast majority of the non-inference time is certainly codegen (the actual runtimes, as we've shown, are <2ms). So while we've improved inferrability, the codegen time has increased substantially. It seems very like that what's happening is the following: with poor inference, codegen has almost nothing to go on and does very little to optimize anything. Now that it knows more about most of the types, it's able to optimize more aggressively, but this increases the amount of time spent on compilation. (The inference time also increases--subtract the exclusive time from the total time and compare--but we aren't worried about that because we know we can cache the inference results through precompilation.)

We have an escape hatch: set the Base.Experimental.@compiler_options to a lower optimization level. The default setting is good for, e.g., numerical code, but IO can probably get by with level 1. (Worth benchmarking, of course.)

Even without this escape hatch, I would say we're still on the right path. Someday, someone will add caching of native code, and then we'll be very glad for the improved inference. You can guess from reduction in number of "direct children" (see output above) that the inference chains are longer in the new version, and that will correspondingly cache more of the native code once that becomes available.

@timholy
Copy link
Member

timholy commented Feb 26, 2021

xref tlnagy/TiffImages.jl#39

We can't claw back all the extra codegen time. Still, I think we're on the right path: having better inference has improved the runtime performance, decreased the chance of being invalidated, (subjectively) led to a cleaner package, and sets the stage for better caching of native code. With the tweak to the optimization level hopefully we'll be roughly in the ballpark of some of the lower latencies you've measured, but I'm willing to sacrifice a little today for the sake of a better future.

@timholy
Copy link
Member

timholy commented Mar 2, 2021

I'm fine with merging this now or after #295, whatever you prefer.

@IanButterworth
Copy link
Member Author

I think TiffImages needs a release once tlnagy/TiffImages.jl#35 has merged before we want to merge this

@timholy
Copy link
Member

timholy commented Mar 2, 2021

👍

I did a release and called it 1.5.0 (there was actually a lot of accumulated stuff), but of course it didn't have #295. So that will be 1.6.0.

@codecov
Copy link

codecov bot commented Mar 4, 2021

Codecov Report

Merging #290 (2382e7f) into master (35ddff1) will increase coverage by 1.17%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #290      +/-   ##
==========================================
+ Coverage   89.59%   90.77%   +1.17%     
==========================================
  Files          10       10              
  Lines         596      726     +130     
==========================================
+ Hits          534      659     +125     
- Misses         62       67       +5     
Impacted Files Coverage Δ
src/registry.jl 91.16% <ø> (+5.32%) ⬆️
src/error_handling.jl 76.66% <0.00%> (-3.34%) ⬇️
src/types.jl 89.47% <0.00%> (+1.23%) ⬆️
src/deprecated.jl 63.04% <0.00%> (+4.34%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 35ddff1...2382e7f. Read the comment docs.

@IanButterworth
Copy link
Member Author

IanButterworth commented Mar 7, 2021

With TiffImages 0.3.0 and ImageIO 0.5.3 out I think this is good to merge. I'll let you orchestrate @timholy given all the recent changes.

Edit: removed example accidentally using png's

@timholy
Copy link
Member

timholy commented Mar 7, 2021

I'll let you orchestrate @timholy given all the recent changes.

That's a nice way of putting it. I'll wait until the bleeding stops before I do anything other than make bugfixes 😢.

@IanButterworth
Copy link
Member Author

Nothing but appreciation coming from here!

Hilariously I got my png's confused with tiff's in my example above.. Trying again..

I'd say the first save here is a little higher than would be expected given we added the precompile helper (the first load includes package load time, so more ok, I think), but perhaps we could let it into the wild and revisit?

(@v1.7) pkg> st FileIO ImageIO TiffImages
      Status `~/.julia/environments/v1.7/Project.toml`
  [5789e2e9] FileIO v1.6.0 `~/Documents/GitHub/FileIO.jl`
  [82e4d734] ImageIO v0.5.3
  [731e570b] TiffImages v0.3.0

julia> using FileIO

julia> [@time FileIO.load("example.tiff") for i=1:3];
  2.644576 seconds (6.57 M allocations: 419.619 MiB, 9.71% gc time, 8.45% compilation time)
  0.030452 seconds (585 allocations: 19.940 MiB)
  0.030031 seconds (585 allocations: 19.940 MiB)

julia> size(ans[1]), eltype(ans[1])
((1536, 1696), ColorTypes.RGBA{FixedPointNumbers.N0f8})

julia> [@time FileIO.save("test.tiff", ans[1]) for i in 1:3];
  1.442666 seconds (4.28 M allocations: 256.335 MiB, 12.86% gc time, 5.54% compilation time)
  0.066298 seconds (4.06 k allocations: 10.015 MiB)
  0.069191 seconds (4.06 k allocations: 10.015 MiB)

@IanButterworth
Copy link
Member Author

@tlnagy any reason to not merge this and put it out in a new 1.7.0 release?

@tlnagy
Copy link
Contributor

tlnagy commented Apr 16, 2021

Not on my end!

I'm slowly making progress towards getting LZW support for TiffImages.jl now that tlnagy/Balloons.jl#3 is done, but I don't think it should hold this up. Correct me if I'm wrong, but FileIO should degrade gracefully if TiffImages.jl can't handle a compressed image (for now).

@IanButterworth
Copy link
Member Author

Great. As long as TiffImages throws an error if it can't handle a compressed image, then it should fall back to the other methods.

I'm going to re-run CI just to double check on master, then merge if successful

@IanButterworth IanButterworth merged commit e49a41e into master Apr 16, 2021
@IanButterworth IanButterworth deleted the ib/tiff_imageio branch April 16, 2021 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants