Скачивание 1000 файлов. Asyncio vs Golang

В этой статье сравним скорость работы и реализацию скачивания 1000 файлов с помощью Python Asyncio и Golang. Файлы загружены на минио, запущенном локально на mac OS 2,3 GHz Intel Core i5, 8 GB 2133 MHz LPDDR3
hw.physicalcpu: 4
hw.logicalcpu: 8
Каждый файл имеет размер 6 мегабайт, а размер всех файлов таким образом составляет 6gb.

Golang
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
)
func downloadFileWorker(urlsQeue chan string, wg *sync.WaitGroup) {
defer wg.Done()
for url := range urlsQeue {
response, err := http.Get(url)
if err != nil {
fmt.Printf("error request to %s\n", url)
panic(err)
}
defer response.Body.Close()
splitURL := strings.Split(url, "/")
fileName := splitURL[len(splitURL)-1]
savePath := "saved_files/" + fileName
file, err := os.Create(savePath)
if err != nil {
fmt.Printf("error creating to %s\n", savePath)
return
}
defer file.Close()
_, err = io.Copy(file, response.Body)
if err != nil {
fmt.Printf("error coping to %s\n", savePath)
}
}
}
func main() {
start := time.Now()
var wg sync.WaitGroup
bucketURL := "http://localhost:9000/files/"
urlsQeue := make(chan string)
for i := 0; i < 100; i++ {
wg.Add(1)
go downloadFileWorker(urlsQeue, &wg)
}
for i := 1; i < 1000; i++ {
url := bucketURL + strconv.Itoa(i) + ".pdf"
urlsQeue <- url
}
close(urlsQeue)
wg.Wait()
duration := time.Since(start)
fmt.Println("Time: ")
fmt.Println(duration)
}
Python
import aiohttp
import aiofiles
import time
import asyncio
async def download_file_worker(queue, session):
while True:
try:
url = queue.get_nowait()
async with session.get(url) as response:
if response.status != 200:
print(f'Failed to download {url}')
data = await response.read()
filename = 'saved_files/' + url.split('/')[-1]
async with aiofiles.open(filename, 'wb') as file:
await file.write(data)
queue.task_done()
except asyncio.QueueEmpty:
print('Queue is empty')
return
async def main():
bucket_url = "http://localhost:9000/files/"
urls = [
bucket_url + str(i) + '.pdf' for i in range(1, 1001)
]
loop = asyncio.get_event_loop()
queue = asyncio.Queue()
for url in urls:
queue.put_nowait(url)
async with aiohttp.ClientSession() as session:
await asyncio.gather(*[download_file_worker(queue, session) for i in range(100)])
if __name__ == '__main__':
start = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
end = time.time()
print(end - start)
Результаты
- golang -
99.590284137s
- asyncio - aiohttp + aiofiles
127.45670866966248
Asyncio показал себя медленнее на 28% от скорости работы на golang. Помимо того для работы с сетью и файлами асинхронно необходимы пакеты aiohttp и aiofiles.