So @fabiangans and I have been having discussions about improving the performance of Zarr.jl. The goal is to improve the time it takes to load data from a remote zarr store (eg on HTTP, GCS, or S3). The current implementation for this is very fast in zarr-python because it uses async, so we have been trying to also start loading chunks from remote storage using async. However we have found that retrieving the chunks async via HTTP.jl is several times slower than through python which is using aiohttp. On my connection which is fairly fast (~3 Gbps), the difference is barely noticeable for retrieving very small files but quickly widens for larger files. In this example each file I download is about ~28 MB.
via aiohttp:
In [24]: keys = [f'analysed_sst/{i}.1.0' for i in range(100)]
In [25]: async def get_items(keys):
...: async with aiohttp.ClientSession() as session:
...: tasks = []
...: for ckey in keys:
...: url = f"https://mur-sst.s3.us-west-2.amazonaws.com/zarr-v1/{ckey}"
...: tasks.append(asyncio.create_task(get_cdata(session, url)))
...: cdatas = await asyncio.gather(*tasks)
...: return cdatas
...:
In [26]: async def get_cdata(session, url):
...: async with session.get(url) as resp:
...: cdata = await resp.read()
...: return cdata
...:
In [27]: %time cdatas = asyncio.run(get_items(keys))
CPU times: user 3.27 s, sys: 1.49 s, total: 4.76 s
Wall time: 8.44 s
via HTTP.jl
julia> urls = map(i->"https://mur-sst.s3.us-west-2.amazonaws.com/zarr-v1/analysed_sst/$i.1.0", 0:99);
julia> @time asyncmap(url->HTTP.request("GET", url, status_exception=false).body, urls);
30.422709 seconds (1.35 M allocations: 4.911 GiB, 1.18% gc time, 0.11% compilation time)
I wanted to post this here in case people know of better ways to do async HTTP requests in Julia, but this (or using @sync … @async macros) seems to be the standard way I see people doing it.
I am no expert on this subject, but my hunch is that the main reason this is much slower is because aiohttp nativel implements even some of the lower level (including reading the response bytes) asynchronously, as explained here. What I would like to know is if there are any better alternative native Julia libraries that could be competitive or if there is a better way to do this in HTTP.jl.