[Gelöst] Sind Go's S3-Listenobjekte langsamer als Python? Problem

Einführung

Beim Herunterladen einer großen Anzahl von Objekten aus S3 wird die Geschwindigkeit unabhängig von der Objektgröße nicht mäßig angezeigt. Als ich in Python schrieb, gab ich mein Bestes mit ** concurrent.futures **, aber vielleicht kann ich es mit Goroutine tun? Ich dachte, ich gab mein Debüt in Golang.

Was ich machen wollte

--Verwenden Sie ListObjectV2, um alle Schlüssel unter dem spezifischen Präfix von S3 abzurufen

Was wirklich passierte

Hmm? Gleiche Geschwindigkeit, weil Sie gerade die API gedrückt haben. Wenn ja, bin ich immer noch überzeugt, aber ich mache mir ein wenig Sorgen, dass ** Go langsamer ist als die Skriptsprache **. Ich änderte schnell meinen Zeitplan und versuchte, diese Angelegenheit ein wenig zu überprüfen.

Quellcode

Go-Version

Also hier und [hier](https: // Ich werde es unter Bezugnahme auf docs.aws.amazon.com/sdk-for-go/api/service/s3/#S3.ListObjectsV2) schreiben.

main.go


package main

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"os"
)

func main() {
	bucket := os.Getenv("BUCKET")
	prefix := os.Getenv("PREFIX")
	region := os.Getenv("REGION")

	sess := session.Must(session.NewSession())
	svc := s3.New(sess, &aws.Config{
		Region: &region,
	})
	params := &s3.ListObjectsV2Input{
		Bucket: &bucket,
		Prefix: &prefix,
	}
	fmt.Println("Start:")
	err := svc.ListObjectsV2Pages(params,
		func(p *s3.ListObjectsV2Output, last bool) (shouldContinue bool) {
			for _, obj := range p.Contents {
				fmt.Println(*obj.Key)
			}
			return true
		})
	fmt.Println("End:")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
}

Python-Version

Ich werde das auch schreiben. Mit einem Low-Level-Client, der die Bedingungen mit Go erfüllt.

main.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import boto3
bucket = os.environ["BUCKET"]
prefix = os.environ["PREFIX"]
region = os.environ["REGION"]

# r = boto3.resource('s3').Bucket(bucket).objects.filter(Prefix=prefix)
# [print(r.key) for r in r]
#Normalerweise bekomme ich es wie oben, aber ich messe es mit dem folgenden Code, um es an Golang zu senden

s3_client = boto3.client('s3', region)

contents = []
next_token = ''
while True:
    if next_token == '':
        response = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix)
    else:
        response = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix, ContinuationToken=next_token)

    contents.extend(response['Contents'])
    if 'NextContinuationToken' in response:
        next_token = response['NextContinuationToken']
    else:
        break

[print(r["Key"]) for r in contents]

Umgebung

Server usw.

Erstellen / Bereitstellen usw.

――Ich möchte die Umwelt nicht verschmutzen und es ist problematisch, deshalb habe ich alles mit Docker erstellt.

$ docker-compose up -d --build

Dockerfile


FROM golang:1.13.5-stretch as build
RUN go get \
  github.com/aws/aws-sdk-go/aws \
  github.com/aws/aws-sdk-go/aws/session \
  github.com/aws/aws-sdk-go/service/s3 
COPY . /work
WORKDIR /work
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main main.go

FROM python:3.7.6-stretch as release
RUN pip install boto3
COPY --from=build /work/main /usr/local/bin/main
COPY --from=build /work/main.py /usr/local/bin/main.py
WORKDIR /usr/local/bin/

docker-compose.yml


version: '3'
services:
  app:
    build:
      context: .
    container_name: "app"
    tty: True
    environment:
      BUCKET: <Bucket>
      PREFIX: test/
      REGION: ap-northeast-1

S3 Eimer

Erstellen Sie einen Bucket in der Region Tokio und erstellen Sie mit den folgenden Tools etwa 1000.

#/bin/bash

Bucket=<Bucket>
Prefix="test"

#Testdateierstellung
dd if=/dev/zero of=testobj bs=1 count=30
#Kopie der Masterdatei
aws s3 cp testobj s3://${Bucket}/${Prefix}/testobj
#Doppelte Master-Datei
for i in $(seq 0 9); do
    for k in $(seq 0 99); do
        aws s3 cp s3://${Bucket}/${Prefix}/testobj s3://${Bucket}/${Prefix}/${i}/${k}/${i}_${k}.obj
    done
done

Messung

Messergebnis (1000 Objekt)

$ time docker-compose exec app ./main

~ Abkürzung ~

real    0m21.888s
user    0m0.580s
sys     0m0.107s
$ time docker-compose exec app ./main.py

~ Abkürzung ~

real    0m2.671s
user    0m0.577s
sys     0m0.104s

Go ist zehnmal langsamer als Python. Warum!

Versuchen Sie, die Anzahl der Objekte zu erhöhen

#Nur Unterschied
for i in $(seq 0 99); do
    for k in $(seq 0 99); do

――Der Upload dauerte übrigens 3-4 Stunden. Ich hätte das Werkzeug richtig machen sollen ...

Ergebnis der erneuten Messung (10000 Objekt)

$ time docker-compose exec app ./main

~ Abkürzung ~

real    0m23.276s
user    0m0.617s
sys     0m0.128s
$ time docker-compose exec app ./main.py

~ Abkürzung ~
real    0m5.973s
user    0m0.576s
sys     0m0.114s

Diesmal beträgt der Unterschied ungefähr das 4-fache. Vielmehr scheint es einen Unterschied von ungefähr 18 Sekunden zu geben, unabhängig von der Anzahl der Objekte. Hmmm.

Am Ende

――Ich habe das Gefühl, dass ich die Sprachspezifikationen aufgrund der Bibliothekseinstellungen nicht verstehe, daher möchte ich weitere Informationen erhalten. ――Wenn die Effizienz der ** parallelen Download-Verarbeitung in Goroutine **, die der ursprüngliche Zweck ist, gut ist, scheint es einen Fehler von etwa 20 Sekunden zu geben, daher werde ich versuchen, den Rest zu implementieren.

Wo man sich Sorgen machen muss

Follow-up (2020/01/18)

Wie im Kommentarbereich empfohlen, habe ich versucht, das SDK zu debuggen Ich habe festgestellt, dass es lange gedauert hat, den IAM-Berechtigungsnachweis zu finden. War der Standardwert des Betriebssystems "-stretch" schlecht? Ich habe es danach mehrmals versucht, aber es ist in dieser Umgebung nicht wieder aufgetaucht, also werde ich es lösen. Es ist launisch, aber ...

@nabeken Danke!

Recommended Posts

[Gelöst] Sind Go's S3-Listenobjekte langsamer als Python? Problem
Golang vs. Python - Ist Golang besser als Python?