Memoryview slicing performance: cause and resolution

Unexpected slowdown when processing binary blobs with memoryview usually appears in real-world pipelines that feed pandas DataFrames from binary files, where developers slice the view inside tight loops. This is caused by each slice allocating a new memoryview object, adding hidden overhead that can cripple throughput.

# Example showing the issue
import time

data = bytes(range(256)) * 1000  # ~256KB buffer
mv = memoryview(data)

start = time.time()
# Bad: slice inside hot loop
total = 0
for i in range(0, len(data), 64):
    chunk = mv[i:i+64]   # creates new memoryview each iteration
    total += sum(chunk)
print('bad loop time:', time.time() - start)

# Good: avoid slicing
start = time.time()
total = 0
for i in range(0, len(data), 64):
    # direct indexing without new slice
    for j in range(64):
        total += mv[i + j]
print('optimized loop time:', time.time() - start)
# Output shows the optimized loop runs noticeably faster

Each memoryview slice constructs a fresh memoryview object and performs bounds checking, which adds Python‑level overhead in tight loops. CPython’s memoryview implementation creates a new wrapper even though the underlying buffer is shared, so the cost scales with the number of slices. This behavior follows the official Python data model for buffer protocol objects. Related factors:

  • Per‑iteration object allocation
  • Bounds checking on every slice
  • Lack of in‑place view manipulation

To diagnose this in your code:

# Run a quick benchmark
import timeit

def bad():
    mv = memoryview(data)
    total = 0
    for i in range(0, len(data), 64):
        total += sum(mv[i:i+64])
    return total

def good():
    mv = memoryview(data)
    total = 0
    for i in range(0, len(data), 64):
        for j in range(64):
            total += mv[i + j]
    return total

print('bad:', timeit.timeit(bad, number=10))
print('good:', timeit.timeit(good, number=10))
# Expect 'bad' to be significantly slower

Fixing the Issue

The quick fix is to avoid creating a new slice in the hot path:

mv = memoryview(data)
total = sum(mv[i] for i in range(len(data)))

When to use: Prototyping, debugging small scripts Trade‑off: Less readable for complex chunk processing

For production code, combine validation with a slice‑free inner loop and explicit length checks:

import logging

mv = memoryview(data)
chunk_size = 64
if len(data) % chunk_size != 0:
    logging.warning('Data length not a multiple of chunk size')

total = 0
for offset in range(0, len(data), chunk_size):
    # Direct buffer access without new memoryview objects
    for i in range(chunk_size):
        total += mv[offset + i]

assert total == sum(data), 'Checksum mismatch after processing'

When to use: Production pipelines, data ingestion into pandas, high‑throughput services Why better: Eliminates per‑iteration allocations, retains a single memoryview, and adds safety checks that catch mis‑aligned buffers early.

What Doesn’t Work

❌ Assigning slice to bytes: chunk = mv[i:i+64].tobytes() – forces a full copy each iteration

❌ Wrapping slice in list: list(mv[i:i+64]) – allocates Python objects unnecessarily

❌ Using NumPy conversion: np.array(mv[i:i+64]) – adds heavy overhead for no benefit

  • Converting slices to bytes with .tobytes() inside loops
  • Using list() on a memoryview slice, creating a Python list of ints
  • Assuming memoryview slicing returns a view without overhead

When NOT to optimize

  • Tiny buffers: Under a few kilobytes, allocation cost is negligible
  • One‑off scripts: Ad‑hoc analysis where readability outweighs speed
  • Already using NumPy: When an ndarray provides the needed view semantics
  • Non‑critical paths: Logging or diagnostic code where performance impact is irrelevant

Frequently Asked Questions

Q: Does slicing a memoryview copy the underlying data?

No, it creates a new view object that references the same buffer.


Memoryview slicing is handy but can become a hidden performance sink in hot loops. By keeping a single view alive and indexing directly, you eliminate unnecessary allocations while preserving the zero‑copy advantage. Apply the validation steps to guard against mis‑aligned buffers and keep your data pipelines fast.

Why Python objects consume excess memoryWhy numpy ravel vs flatten affect memory usageWhy NumPy strides affect memory layoutWhy numpy boolean indexing spikes memory