ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 32

  1. Blog

array.array vs list[int] vs bytes Lab

A hands-on Linux localhost lab comparing memory density and numeric throughput for list[int], array.array('i'), bytearray, and memoryview.

Aditya Challa·30 September 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Structures
  3. Lab topology
  4. Memory density
  5. Lead table — throughput (p50)
  6. Reading it
  7. Pitfalls
  8. When to pick what
  9. Reproduce
  10. Closing

Intro — what this post promises

Storing millions of integers: list[int], array.array('i'), or a packed bytearray / memoryview? Folklore says arrays are “more compact and faster.” This lab measures bytes per element and sum / iterate / index ops/s on Linux localhost.

Related links:

  • deque vs list queue localhost lab
  • itertools vs python loops localhost lab
  • dataclass vs slots vs dict localhost lab
  • struct pack vs to_bytes localhost lab
  • copy vs deepcopy localhost lab
  • lru_cache hit vs miss localhost lab
  • set vs list membership localhost lab
  • json vs orjson vs msgpack localhost lab

Lab honesty (1 Oct 2026 IST): Python 3.13.5. N=2,000,000. Affiliates: 0. Density uses itemsize / estimated sys.getsizeof — not a full heap profiler. sum(list) is a C-accelerated special case.

Verdict up front: density wins for array('i') — 4 B/elem vs list ~36 B/elem (~9×). Throughput is the twist: sum(list) ~196M/s beats sum(array) ~102M because each C int becomes a Python int. Manual bytearray unpack is ~5.8M/s.


Structures

KindLayout
list(range(N))pointers to PyLong objects
array.array('i', …)contiguous signed 32-bit
bytearray of tobytes()raw little-endian i32
memoryview(ba).cast('i')typed view over bytes

Lab topology

N = 2_000_000
Throughput: sum(), for-x-in, stride-7 index
Metric: p50 ops/s (elements or probes)
Density: array.itemsize / sizeof estimate

Script: lab-evidence/56-array-vs-list-ints/results/run_lab.py.


Memory density

Structurebytes / elem (approx)
array('i')4
bytearray (i32 pack)4
list[int]~36

sys.getsizeof on the list object alone is tiny; the ~36 B figure includes a sample of int object sizes × N. That is the RAM story for large numeric vectors.


Lead table — throughput (p50)

Armops/sns/op
sum(list)195,746,1435.1
sum(array 'i')102,095,8349.8
sum(memoryview 'i')88,346,73411.3
sum(bytearray unpack)5,808,188172.2
for+= list52,402,41819.1
for+= array37,159,10426.9
for+= memoryview35,689,75528.0
index stride7 list24,480,31340.8
index stride7 array24,558,48440.7
index stride7 mv23,070,92543.3

Reading it

  • RAM: array('i') / packed bytes are ~9× denser than a list of small ints. That alone can decide caching and GC pressure.
  • sum(list) is not a fair “list is faster” lesson — CPython’s sum on lists of ints is specialized. Fairer: the for x in …: s += x arms, where list is still ahead (~1.41×) because array iteration boxes.
  • Index stride is nearly tied — boxing happens either way when you pull a Python int out.
  • Hand-unpacking bytearray is for I/O buffers, not inner-loop math.

For numeric crunching that stays in C (NumPy), you leave Python’s boxing tax entirely. This lab is stdlib only.


Pitfalls

  1. Choosing array for speed of sum() — memory was the win; sum(list) can win.
  2. array('l') / platform sizes — 'i' is 4 bytes here; check itemsize.
  3. Assuming memoryview is free — iteration still produces Python ints.
  4. RSS peak noise — prefer buffer nbytes for density claims.

When to pick what

NeedPrefer
Millions of ints, RAM-boundarray.array / packed buffer
App logic, small Nlist
Wire / file bytesbytearray / memoryview
Heavy numeric computeNumPy / similar (out of scope)

Reproduce

python3 lab-evidence/56-array-vs-list-ints/results/run_lab.py

Evidence: /workspace/lab-evidence/56-array-vs-list-ints/results/.


Closing

Compact ≠ always faster in pure Python. On this box array('i') is 4 B/elem vs list ~36 B, but sum(list) hits ~196M/s vs array ~102M. Use arrays for memory; don’t expect them to beat list iteration without staying in C.

array.arraylist of intsbytearraymemoryviewpython memorylocalhost labsrenumeric

Lab evidence

What I found running this

Lab 1 Oct 2026 IST. Ran Python 3.13.5 with N=2,000,000. Measured array('i') at 4 B/elem, list at about 36 B/elem, and bytearray i32 at 4 B/elem. sum(): list 196M/s, array 102M/s, memoryview 88M/s, bytearray unpack 5.8M/s; iter+= list 52M/s vs array 37M/s. Affiliates: 0. Evidence: lab-evidence/56-array-vs-list-ints/.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 87

    mmap vs read Byte-Sum Scan: Localhost Lab

    Hands-on mmap vs read vs chunked sequential byte-sum scan lab: real MB/s on a generated fixture (not page-touch), measured on Linux localhost for SREs.

    30 Sept 2026

  • Plate 72

    bytes vs bytearray: Mutate/Copy Lab

    Hands-on bytes vs bytearray lab: real ops/s for append/extend/slice/copy and when copy dominates over mutate, benchmarked on Linux localhost for SREs.

    30 Sept 2026

  • Plate 16

    struct.pack vs to_bytes vs memoryview Lab

    Benchmarking fixed-record packing with struct.pack, Struct.pack_into, int.to_bytes, and memoryview on Linux localhost.

    Observability & SRE · 30 Sept 2026

On this page

  1. Intro — what this post promises
  2. Structures
  3. Lab topology
  4. Memory density
  5. Lead table — throughput (p50)
  6. Reading it
  7. Pitfalls
  8. When to pick what
  9. Reproduce
  10. Closing
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove