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): DefinesCopyCordToString, which efficiently converts Cord data tostd::stringby reusing the destination's capacity when possible, avoiding redundant allocations.absl/strings/cord.cc: Contains the core mutation logic includingAppendPreciseandPrependPrecisethat 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 likeRemoveCrcNodeandSkipCrcNodethat 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::Cordideal 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →