Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

tarfile: extractfile(member).read() of a sparse member is slow due to repeated bytes concatenation

Ouverte Adaptée aux débutants
#160,774 2 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

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
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Active
Stack technique
python
Domaine
backend

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

performance stdlib triaged type-bug

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

Ouvrir dans Codespaces

Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de python/cpython

Toutes les issues de python/cpython

Issues similaires

Plus d'issues Python

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.