How to Configure Shared Stack Mode in libfiber to Reduce Memory Usage
Enable shared stack mode by setting the ACL_FIBER_ATTR_SHARE_STACK flag via acl_fiber_attr_setsharestack(), passing true to fb.start() in C++, or using the go_share(size) macro to reuse a single stack buffer across all fibers, cutting memory usage from megabytes per fiber to a single shared buffer.
The iqiyi/libfiber library supports high-performance coroutines with a memory-efficient shared stack mode that eliminates per-fiber stack allocations. Unlike the default private-stack mode where each fiber reserves its own memory region, shared stack mode allocates one global buffer that all fibers reuse during execution. This guide demonstrates the exact API calls and source files needed to configure this feature in both C and C++.
How Shared Stack Mode Works
In the default configuration, libfiber allocates a private stack for every fiber (typically 320 KB per fiber), which consumes massive memory when spawning thousands of concurrent tasks. Shared stack mode reverses this model by maintaining a single memory region that acts as a temporary workspace for whichever fiber is currently executing.
When a fiber yields or blocks, libfiber swaps the current stack contents into a save buffer and restores the next fiber's stack into the shared region. This approach requires only enough memory to hold the deepest call stack among active fibers, plus small per-fiber metadata structures. According to the implementation in c/include/fiber/fiber_base.h, the flag ACL_FIBER_ATTR_SHARE_STACK (lines 13–15) controls this behavior, while acl_fiber_attr_setsharestack() (lines 20–21) provides the runtime interface.
Configuring Shared Stack Mode in C
The C API exposes granular control over stack sharing through attribute structures and global size setters defined in c/include/fiber/fiber_base.h.
Setting the Global Shared Stack Size
Before creating any fibers, optionally adjust the shared buffer size from its default of 1,024,000 bytes (1 MB) using the global setter. This buffer must accommodate the maximum recursion depth of any fiber in your application.
#include "fiber/lib_fiber.h"
// Reduce shared buffer to 512 KB before any fiber creation
acl_fiber_set_shared_stack_size(512 * 1024);
The getter and setter prototypes reside in fiber_base.h (lines 30–34), with the underlying storage defined in the library's internal state.
Creating Fibers with Shared Stack Attributes
To enable sharing for a specific fiber, initialize an ACL_FIBER_ATTR structure and set the share flag before creation. The implementation in c/src/fiber.c (lines 713–720) handles the flag assignment.
#include "fiber/lib_fiber.h"
static void fiber_task(ACL_FIBER *fb, void *ctx) {
// Fiber logic executes using the shared buffer
printf("Fiber %u running on shared stack\n", acl_fiber_id(fb));
}
int main(void) {
// Optional: Adjust global buffer size first
acl_fiber_set_shared_stack_size(256 * 1024);
ACL_FIBER_ATTR attr;
acl_fiber_attr_init(&attr); // Initialize defaults
acl_fiber_attr_setsharestack(&attr, 1); // Enable shared mode
// Create 10,000 fibers sharing one stack buffer
for (int i = 0; i < 10000; ++i) {
acl_fiber_create2(&attr, fiber_task, NULL);
}
acl_fiber_schedule(); // Start the event loop
return 0;
}
Configuring Shared Stack Mode in C++
The C++ wrapper in cpp/include/fiber/fiber.hpp provides ergonomic methods that mirror the C API while maintaining object-oriented semantics.
Using the fiber::start Method
When starting a fiber instance, pass true as the second argument to the start() method. The signature in fiber.hpp (lines 55–60) accepts a stack size and a boolean share flag.
#include "fiber/libfiber.hpp"
class Worker : public acl::fiber {
protected:
void run() override {
printf("Worker %u executing\n", self());
}
};
int main() {
// Set global shared buffer to 512 KB
acl::fiber::set_shared_stack_size(512 * 1024);
// Launch 10,000 fibers with shared stack mode
for (int i = 0; i < 10000; ++i) {
Worker* w = new Worker();
w->start(256 * 1024, true); // true enables shared stack
}
acl::fiber::schedule();
return 0;
}
Static helper methods acl::fiber::set_shared_stack_size() and acl::fiber::get_shared_stack_size() (lines 60–71 in fiber.hpp) wrap the underlying C functions for convenient class-level access.
Using the go_share Macro
For lambda-based fiber creation, libfiber provides the go_share(size) macro defined in cpp/include/fiber/go_fiber.hpp (lines 37–38). This macro expands to acl::go_fiber(size, true), automatically enabling shared stack mode without manual attribute handling.
#include "fiber/go_fiber.hpp"
#include <memory>
void concurrent_counter() {
auto counter = std::make_shared<int>(0);
// Spawn 10 fibers sharing a 128 KB stack buffer
for (int i = 0; i < 10; ++i) {
go_share(128 * 1024)[counter] {
(*counter)++;
};
}
acl::fiber::schedule();
printf("Final count: %d\n", *counter);
}
Memory Usage Comparison
The difference between private and shared stack modes becomes dramatic at scale:
- Private stack mode: 10,000 fibers × 320 KB default stack = ~3.2 GB RAM
- Shared stack mode: 10,000 fibers × ~64 bytes metadata + 1 MB shared buffer = ~1.6 MB RAM
The trade-off requires that fibers yield cooperatively; if a fiber blocks without yielding, other fibers cannot execute because the shared buffer remains occupied. This constraint aligns naturally with libfiber's coroutine model where fibers surrender control during I/O waits or explicit yields.
Reference Implementation
The repository includes a production-ready demonstration in samples/cxx/shared_stack/main.cpp. This sample creates 10,000 fibers using go_share(1024) and verifies that all execution contexts operate within a single 1 MB buffer, printing the final counter value to confirm successful shared-stack operation.
Summary
- Shared stack mode reduces memory from megabytes per fiber to a single reusable buffer by setting the
ACL_FIBER_ATTR_SHARE_STACKflag. - Configuration options include
acl_fiber_attr_setsharestack()in C,fb.start(size, true)in C++, and thego_share(size)macro for lambda syntax. - Buffer sizing is controlled globally via
acl_fiber_set_shared_stack_size()(default 1,024,000 bytes) defined inc/include/fiber/fiber_base.h. - Implementation references span
c/src/fiber.c(lines 713–720) for C logic andcpp/include/fiber/fiber.hpp(lines 55–71) for C++ wrappers. - Practical validation is available in
samples/cxx/shared_stack/main.cpp, demonstrating 10,000 concurrent fibers in under 2 MB total memory.
Frequently Asked Questions
What is the default size of the shared stack buffer in libfiber?
The default shared stack buffer size is 1,024,000 bytes (approximately 1 MB), as defined in the implementation of acl_fiber_set_shared_stack_size() and acl_fiber_get_shared_stack_size() in c/include/fiber/fiber_base.h (lines 30–41). You should adjust this value before creating any fibers to match your application's maximum call depth.
Can I mix shared-stack and private-stack fibers in the same program?
Yes, libfiber supports mixing both modes simultaneously. The shared-stack flag is set per-fiber via ACL_FIBER_ATTR or the boolean parameter in acl::fiber::start(), allowing some fibers to use private stacks while others reuse the global buffer. Each fiber operates independently regardless of the stack mode chosen for its peers.
What happens if a fiber's stack usage exceeds the shared buffer size?
If a fiber's recursion depth exceeds the configured shared stack size, the program will encounter stack overflow or memory corruption, similar to exceeding a private stack limit. The buffer does not dynamically expand, so you must profile your deepest call chain and set the global size using acl_fiber_set_shared_stack_size() accordingly before spawning fibers.
Does shared stack mode affect performance compared to private stacks?
Shared stack mode introduces a context-switch overhead due to stack copying when fibers yield. When switching contexts, libfiber must save the current fiber's stack contents from the shared buffer to its private save area, then restore the incoming fiber's stack. However, for I/O-bound workloads typical of coroutines, this copying cost is negligible compared to the memory savings achieved.
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 →