GraphBit (Rust core + workflow graph + lock‑free concurrency)
- Execution engine
- Compiled Rust core schedules a WorkflowGraph with dependency awareness, spawning ready nodes concurrently
- Per‑node‑type concurrency with atomics (no global semaphore); fast‑path skips permits for simple nodes
- Python/Node bindings delegate to the Rust executor (low overhead orchestration)
- What this means
- Lower orchestration overhead, predictable scheduling, high throughput under load
- GraphBit scheduling of dependency‑ready nodes
// Select nodes whose dependencies are all completed
let mut ready_ids: Vec<NodeId> = Vec::new();
for nid in remaining.iter() {
let deps = graph_clone.get_dependencies(nid);
if deps.iter().all(|d| completed.contains(d)) {
`ready_ids.push(nid.clone());`
}
}
- Spawning tasks with selective permit acquisition (fast path)
// Lightweight spawn; skip permits for non-agent nodes
let _permits = if matches!(node.node_type, NodeType::Agent { .. }) {
Some(concurrency_manager.acquire_permits(&task_info).await?)
} else { None };
- Lock‑free per‑node concurrency (no global semaphore)
struct NodeTypeConcurrency {
max_concurrent: usize,
current_count: Arc<std::sync::atomic::AtomicUsize>,
wait_queue: Arc<tokio::sync::Notify>,
}
- Python binding calls into core executor (no Python orchestration loop)
let executor = match config.mode {
ExecutionMode::HighThroughput => CoreWorkflowExecutor::new_high_throughput()
`.with_default_llm_config(llm_config),`
ExecutionMode::LowLatency => CoreWorkflowExecutor::new_low_latency()
`.with_default_llm_config(llm_config)`
`.without_retries()`
`.with_fail_fast(true),`
// ...
};
Other frameworks (Python‑centric orchestration models)
- LangChain
- Model: Chains invoked from Python; parallelism via asyncio with semaphores around ainvoke
- Implication: Python event loop controls orchestration; concurrency limited by interpreter scheduling and per‑call overhead
sem = asyncio.Semaphore(concurrency)
async def run_with_sem(t: str):
async with sem:
`return await chain.ainvoke({"task": t})`
results = await asyncio.gather(*[run_with_sem(task) for task in PARALLEL_TASKS])
- LangGraph
- Model: Declarative state graph; async graph.ainvoke drives node execution via Python
- Implication: Python orchestrates graph transitions and concurrency
graph = self.graphs["simple"]
initial_state = {"messages": [], "task": SIMPLE_TASK_PROMPT, "result": None}
result = await graph.ainvoke(initial_state)
- PydanticAI
- Model: Agent.run executes sequential/async steps within Python
- Implication: Concurrency and orchestration occur in Python agents
agent: Agent = self.agents["simple"]
result = await agent.run(SIMPLE_TASK_PROMPT)
- LlamaIndex
- Model: Direct LLM calls (acomplete) with parallelism via asyncio semaphores
- Implication: Python controls batching and concurrency
sem = asyncio.Semaphore(concurrency)
async def run_with_sem(text: str):
async with sem:
`return await llm.acomplete(text)`
results = await asyncio.gather(*[run_with_sem(t) for t in CONCURRENT_TASK_PROMPTS])
- CrewAI
- Model: Define Agents and Tasks; kickoff runs crews; concurrency via asyncio + run_in_executor
- Implication: Python concurrency governs; crew execution offloaded to thread pool where needed
task = Task(description=task_desc, agent=agent, expected_output="...")
crew = Crew(agents=[agent], tasks=[task], verbose=False)
return await asyncio.get_event_loop().run_in_executor(None, crew.kickoff)
Key technical differences at a glance
- Orchestration layer
- GraphBit: Rust executor drives the workflow graph; Python/Node just configure/dispatch
- Others: Python async/threads orchestrate chains/graphs/agents
- Concurrency control
- GraphBit: Per‑node‑type atomic counters, notify queues, selective permits (reduced contention)
- Others: asyncio semaphores and gather patterns, often per‑pipeline, under Python runtime
- Scheduling model
- GraphBit: Dependency‑aware ready‑set scheduling and lock‑free permits in the core
- Others: Framework‑specific graph/chain semantics, executed via Python event loop
Top comments (0)