How absl::Cord Achieves Better Performance Than std::string for Large Strings

absl::Cord uses a chunked, reference-counted tree structure to provide O(1) append, prepend, and copy operations for large strings, avoiding the reallocations and memory copies required by std::string's contiguous buffer.

For applications handling large, mutable text in the abseil/abseil-cpp library, absl::Cord delivers significant performance advantages over std::string by abandoning the single contiguous buffer model. Instead, it stores data in a tree of chunks that enables constant-time mutations at the ends and cheap sharing between instances. This design fundamentally changes the complexity characteristics of string operations for large datasets.

Chunked Architecture: The Foundation of absl::Cord Performance

While std::string maintains a single contiguous memory block, absl::Cord organizes data as a tree of immutable chunks (often called "flats" or nodes). According to the source comments in absl/strings/cord.h (lines 31-41), this structure specifically optimizes three scenarios: efficient insertions at the beginning or end, zero-copy external memory references, and cheap copy-on-write sharing.

O(1) Append and Prepend Operations

Appending to a large std::string requires O(N) time when reallocation occurs, as the entire buffer must be copied to new memory. In contrast, absl::Cord achieves O(1) amortized complexity by adding new chunks to the tree or filling spare capacity in existing nodes without disturbing existing data.

The implementation in absl/strings/cord.cc demonstrates this through AppendPrecise (lines 9-18), which writes directly into an existing inline buffer or appends a new flat chunk without touching the rest of the data. Similarly, PrependPrecise (lines 21-32) mirrors this logic for front insertions, making both operations constant-time regardless of the Cord's total size.

Copy-on-Write and Cheap Substring Operations

Copying a large std::string always allocates new memory and copies every byte (O(N)). absl::Cord copies are O(1) because they simply increment reference counts on the underlying chunk tree. As documented in absl/strings/cord.h (lines 40-44), this copy-on-write semantics means multiple Cords can share the same underlying data until a mutation actually requires a copy.

Substrings (created via Subcord) are equally efficient, representing views over existing chunks rather than new allocations. This makes extracting portions of multi-gigabyte strings nearly instantaneous.

Performance Comparison: absl::Cord vs std::string

The trade-offs between these containers become clear when examining operation complexity:

Feature absl::Cord std::string
Appending / Prepending O(1) amortized – adds new chunks without copying existing data O(N) – requires reallocation and full buffer copy when capacity exceeded
Copy / Assignment O(1) – reference count increment only O(N) – allocates new buffer and copies all data
Substring O(1) – tree view over existing chunks O(N) – allocates new buffer for substring
External Memory Zero-copy via MakeCordFromExternal Always copies data into owned buffer
Random Access O(log N) – traverses chunk tree O(1) – direct pointer arithmetic
Memory Overhead Higher (tree metadata, reference counts) Lower (single pointer + size/capacity)

Zero-Copy External Memory Integration

A unique advantage of absl::Cord is the ability to reference externally allocated memory without copying. The MakeCordFromExternal function wraps existing buffers in a Cord interface with a custom deletion callback. This is impossible with std::string, which always assumes ownership and copies data into its own allocation.

This capability is particularly valuable when interfacing with memory-mapped files, network buffers, or C APIs where the data lifecycle is managed externally.

Implementation Details in Abseil Source Code

The performance characteristics stem from specific implementation choices across several key files:

  • absl/strings/cord.h (lines 19-27): Defines CopyCordToString, which efficiently converts Cord data to std::string by reusing the destination's capacity when possible, avoiding redundant allocations.
  • absl/strings/cord.cc: Contains the core mutation logic including AppendPrecise and PrependPrecise that implement the O(1) end operations.
  • absl/strings/internal/cord_rep_flat.h: Defines flat chunk objects used when appending large amounts of data without creating deep tree structures.
  • absl/strings/internal/cord_rep_btree.h: Implements the B-tree representation that enables O(log N) navigation while maintaining O(1) end operations.
  • absl/strings/internal/cord_internal.h: Provides low-level helpers like RemoveCrcNode and SkipCrcNode that keep the chunk tree efficient during mutations.

Practical Examples: Using absl::Cord for Large Strings

#include "absl/strings/cord.h"
#include <string>
#include <iostream>

int main() {
    // 1. Build a huge cord by repeatedly appending without reallocations.
    absl::Cord big;
    for (int i = 0; i < 1'000'000; ++i) {
        big.Append("x");               // O(1) per iteration
    }
    std::cout << "size: " << big.size() << '\n';

    // 2. Zero-copy external memory.
    const char* data = "already-allocated payload";
    absl::Cord external = absl::MakeCordFromExternal(
        absl::string_view(data, strlen(data)),
        [](absl::string_view) { /* no-op releaser */ });
    // No copy of `data` happened.

    // 3. Cheap copy / sharing.
    absl::Cord copy = big;             // Only ref-count increments (O(1))
    std::cout << "copy size: " << copy.size() << '\n';

    // 4. Fast sub-cord (view) without copying.
    absl::Cord view = big.Subcord(100, 200);  // O(1) view over the same chunks
    std::cout << "view size: " << view.size() << '\n';

    // 5. Convert to std::string efficiently if we already have capacity.
    std::string dst;
    dst.reserve(big.size());           // allocate once
    absl::CopyCordToString(big, &dst); // reuses capacity, no extra allocs
    std::cout << "dst length: " << dst.size() << '\n';
}

Appending, prepending, and sub-cord operations remain cheap because they manipulate the underlying chunk tree rather than moving large blocks of memory. The external-memory example demonstrates avoiding any copy at all, something a plain std::string cannot achieve.

Summary

  • Chunked storage eliminates O(N) reallocations during growth, making absl::Cord ideal for incrementally building large strings.
  • O(1) copy and assignment via reference counting allows efficient passing and sharing of large immutable datasets.
  • Zero-copy external memory integration enables Cords to reference existing buffers without allocation overhead.
  • Fast end modifications (append/prepend) operate in constant time regardless of string size, unlike std::string's linear complexity.
  • Trade-off: Random access is O(log N) rather than O(1), and memory overhead is higher due to tree metadata.

Frequently Asked Questions

When should I choose absl::Cord over std::string?

Use absl::Cord when working with strings larger than a few kilobytes that undergo frequent appending, prepending, or copying. It is also superior when you need to share large immutable data between multiple objects or reference external memory without copying. For small strings or heavy random-access workloads, std::string remains the better choice due to cache locality and O(1) indexing.

How does absl::Cord handle memory management?

absl::Cord uses an intrusive reference-counting system on its internal nodes (chunks). When you copy a Cord, the implementation increments atomic reference counts on the underlying tree nodes rather than allocating new memory. When the last reference is destroyed, the nodes are deallocated. This copy-on-write mechanism ensures that mutations only copy the specific chunks being modified, not the entire data structure.

Is random access slower with absl::Cord?

Yes. Because data is distributed across multiple chunks, accessing character i requires traversing the tree to locate the correct chunk, resulting in O(log N) complexity compared to std::string's O(1) pointer arithmetic. For applications requiring intensive character-by-character iteration or frequent arbitrary indexing, std::string provides better cache performance and lower latency.

Can I convert absl::Cord to std::string without copying?

You cannot avoid copying the actual character data when converting to std::string because std::string requires contiguous storage. However, you can avoid extra allocations by using absl::CopyCordToString, defined in absl/strings/cord.h (lines 19-27). If the destination std::string already has sufficient capacity reserved, this function writes directly into that buffer, preventing the double-allocation that would occur with a naive copy.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →