tarfile: extractfile(member).read() of a sparse member is slow due to repeated bytes concatenation
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 75/100
Piste de recherche
Lisez _FileInFile.read() dans Lib/tarfile.py et suivez la façon dont les segments de données et les trous clairsemés sont ajoutés. Examinez les tests tarfile existants pour les membres clairsemés, puis ajoutez des tests pour extractfile().read() et les lectures par blocs, en vérifiant le contenu attendu. Le travail est terminé lorsque les deux modes de lecture renvoient le contenu correct et que la lecture complète évite les concaténations répétées de bytes.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Bug report
_FileInFile.read() in Lib/tarfile.py builds its result with buf += b for each data segment and buf += NUL * length for each hole of a sparse member. Each bytes concatenation copies the whole accumulated buffer, so reading a sparse member with TarFile.extractfile(member).read() takes time proportional to the member size times the number of sparse segments.
Holes take no space in the archive, so a small archive is enough to make this noticeable. extract(), extractall() and reading the member in small chunks (for example with shutil.copyfileobj()) are not affected.
Reproducer
import io, tarfile, time
def build(n, blk=4096):
# GNU sparse 0.1 (pax) member: n data blocks separated by holes.
t = tarfile.TarInfo("GNUSparseFile.0/a.img")
t.size = n * blk
t.pax_headers = {
"GNU.sparse.map": ",".join(f"{2 * i * blk},{blk}" for i in range(n)),
"GNU.sparse.name": "a.img",
"GNU.sparse.realsize": str(2 * n * blk),
}
bio = io.BytesIO()
with tarfile.open(fileobj=bio, mode="w", format=tarfile.PAX_FORMAT) as tf:
tf.addfile(t, io.BytesIO(b"D" * t.size))
return bio.getvalue()
def read_time(n):
with tarfile.open(fileobj=io.BytesIO(build(n))) as tf:
f = tf.extractfile(tf.next())
t0 = time.perf_counter()
f.read()
return time.perf_counter() - t0
for n in (1000, 2000, 4000):
print(f"{n} segments: {read_time(n):.2f}s")
On a release build (macOS, Apple M3) the read of a sparse member with 4000 segments takes several seconds; the time roughly quadruples when the segment count doubles. With the change below each read is a fraction of a second.
Suggested fix
Accumulate the pieces in a list and join once, instead of repeated concatenation:
- buf = b""
+ chunks = []
while size > 0:
...
- buf += b
+ chunks.append(b)
else:
- buf += NUL * length
+ chunks.append(NUL * length)
size -= length
self.position += length
- return buf
+ return b"".join(chunks)
I have a branch with this change and a regression test that reads a multi-segment sparse member through extractfile().read() and in chunks, and compares both to the expected content. I can open a PR if this looks right.
- Langage dominant
- Python
- Étoiles
- 77.8k
- Forks
- 37.7k
- Merge moyen
- 12 h 27 min
- PR mergées (30 j)
- 710
Préparer son environnement
Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de python/cpython
-
3.16 build type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
python/cpython#160829 · 4 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
dependencies infra topic-ensurepip
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
Les mainteneurs répondent en général sous 1 jour
-
symtable: repr() of a Symbol raises TypeError under -X lazy_imports=allPeut-être pris Une pull request liée à cette issue est ouverte ou déjà fusionnée. Ouvertestdlib topic-lazy-imports type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
python/cpython#160810 · 3 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
tests topic-profiling type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
python/cpython#160334 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
`iso2022_jp_3` encodes U+9B1D but decodes the result as U+9B1CPeut-être pris @kinqsradio l’a pris il y a 3 jours. Ouverteextension-modules type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de python/cpython
Issues similaires
-
factory
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Arize-ai/phoenix#17420 · 1 commentaire · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
FireDynamics/LEDSmokeAnalysis#176 ·
Les mainteneurs répondent en général sous 1 jour
-
PC
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
Les mainteneurs répondent en général sous 1 jour
-
Make `ocifs` optionalOuvertedependencies
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
dlt-contrib/dlt-filesystem#51 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
Les mainteneurs répondent en général sous 1 jour