Apache License 2.0

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

深度技术分析:bloomberg/quantum

项目背景与痛点

在高并发和大吞吐量的环境下,传统的线程调度机制难以满足复杂业务场景下的性能需求,这也是为什么多线程协程调度器和并行执行引擎在现代分布式系统中愈渐重要。bloomberg/quantum 项目提供了一个强大的多线程协程调度器和并行执行引擎,以高效地处理大规模并发任务,提高程序的执行效率和吞吐量。该工具解决了以下几个痛点:

  • 高并发环境下的资源调度问题。
  • 提升任务执行的可观察性和可管理性。
  • 简化并行任务的编程模型。

核心技术揭秘

技术架构

bloomberg/quantum 运用了多个关键技术栈和设计模式实现其优秀的性能:

  • 协程调度器:quantum 核心引擎是一个高度优化的协程调度器,使用异步非阻塞模型来调度任务。通过将任务划分为多个协程,并使用事件循环来调度这些协程,quantum 可以高效地处理大量并发任务。
  • 并行执行模型:采用细粒度的并行执行策略,利用多线程提高整体执行效率。它能够在多个线程之间动态平衡任务负载,确保系统资源得到充分利用。
  • 无锁数据结构:针对高并发环境,项目采用了无锁数据结构,在多线程环境下减少同步带来的性能开销。
数据流与工作流程
graph TD A[用户提交任务] --> B[任务处理器] B --> C{"协程创建"} C -->|"创建成功"| D[添加到调度队列] D --> E[线程池调度] E --> F{"线程执行"} F -->|"执行完成"| G[写日志/通知] C -->|"创建失败"| H[返回错误] H --> I[处理错误] F -->|"执行失败"| J[异常处理] J --> K[重试/失败通知]

对任务流的理解和管理

quantum 使用任务流管理来确保任务在不同线程间有序、无阻塞地传递。这通过结合事件循环模型和状态机实现,每个任务由其状态机驱动,而事件循环负责调度各个状态。

功能亮点与差异

相对于其他竞品,bloomberg/quantum 的优势体现在以下几点:

  • 更低的系统级开销:相较于使用线程来完成任务,协程的切换更加高效。
  • 出色的可伸缩性:支持大规模的软件架构,可以轻松扩展到数百甚至上千个并发任务。
  • 更高的灵活性和可配置性:用户可根据需要自定义和参数化协程调度,使其更契合特定的应用场景。

应用场景与落地建议

作为一种高性能的解决方案,bloomberg/quantum 适用于需要处理大量短任务请求的场景,例如:

  • 大数据处理:用于大规模数据流的处理。
  • 网络服务:适用于并发请求高的应用场景,如后端服务、中间件等。
  • 金融应用:实时数据处理、高频交易中有着广泛的应用空间。

在生产环境中使用时,应注意:

  • 资源监控:合理调整和监控调度器中的协程数量,避免资源穷尽导致的服务不可用。
  • 错误处理:对于复杂任务的错误可能需要逐步细化方案,确保任务的稳定完成。

综合评价

综上所述,bloomberg/quantum 项目在当代高并发环境下展现出了强大的性能和灵活性,是解决复杂任务调度问题的有力工具。其优点在于高效而灵活的并发模型,能与现代分布式系统无缝结合;不足之处则在于对开发者的使用成本,特别是在性能优化和错误处理方面这一曲线陡峭。总的来说,该项目体现了 Bloomberg 在高性能计算领域深厚的技术积累。

关于

GitHub 原始描述(英文)

Powerful multi-threaded coroutine dispatcher and parallel execution engine

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

简要分析

热度分
883
价值分
421
活跃状态
活跃

语言占比

C
C++
CMake

Release

README

Quantum Library : A scalable C++ coroutine framework

Build status

Quantum is a full-featured and powerful C++ framework build on top of the Boost coroutine library. The framework allows users to dispatch units of work (a.k.a. tasks) as coroutines and execute them concurrently using the ‘reactor’ pattern.

Features

  • NEW Added support for simpler V2 coroutine API which returns computed values directly.
  • Header-only library and interface-based design.
  • Full integration with Boost asymmetric coroutine library.
  • Highly parallelized coroutine framework for CPU-bound workloads.
  • Support for long-running or blocking IO tasks.
  • Allows explicit and implicit cooperative yielding between coroutines.
  • Task continuations and coroutine chaining for serializing work execution.
  • Synchronous and asynchronous dispatching using futures and promises similar to STL.
  • Support for streaming futures which allows faster processing of large data sets.
  • Support for future references.
  • Cascading execution output during task continuations (a.k.a. past futures).
  • Task prioritization.
  • Internal error handling and exception forwarding.
  • Ability to write lock-free code by synchronizing coroutines on dedicated queues.
  • Coroutine-friendly mutexes and condition variables for locking critical code paths or synchronizing access to external objects.
  • Fast pre-allocated memory pools for internal objects and coroutines.
  • Parallel forEach and mapReduce functions.
  • Various stats API.
  • Sequencer class allowing strict FIFO ordering of tasks based on sequence ids.

Sample code

Quantum is very simple and easy to use:

using namespace Bloomberg::quantum;

// Define a coroutine
int getDummyValue(CoroContextPtr<int> ctx)
{
    int value;
    ...           //do some work
    ctx->yield(); //be nice and let other coroutines run (optional cooperation)
    ...           //do more work and calculate 'value'
    return ctx->set(value);
}

// Create a dispatcher
Dispatcher dispatcher;

// Dispatch a work item to do some work and return a value
int result = dispatcher.post(getDummyValue)->get();

Chaining tasks can also be straightforward. In this example we produce various types in a sequence.

using namespace Bloomberg::quantum;

// Create a dispatcher
Dispatcher dispatcher;

auto ctx = dispatcher.postFirst([](CoroContextPtr<int> ctx)->int {
    return ctx->set(55); //Set the 1st value
})->then([](CoroContextPtr<double> ctx)->int {
    // Get the first value and add something to it
    return ctx->set(ctx->getPrev<int>() + 22.33); //Set the 2nd value
})->then([](CoroContextPtr<std::string> ctx)->int {
    return ctx->set("Hello world!"); //Set the 3rd value
})->finally([](CoroContextPtr<std::list<int>> ctx)->int {
    return ctx->set(std::list<int>{1,2,3}); //Set 4th value
})->end();

int i = ctx->getAt<int>(0); //This will throw 'FutureAlreadyRetrievedException'
                            //since future was already read in the 2nd coroutine
double d = ctx->getAt<double>(1); //returns 77.33
std::string s = ctx->getAt<std::string>(2); //returns "Hello world!";
std::list<int>& listRef = ctx->getRefAt<std::list<int>>(3); //get list reference
std::list<int>& listRef2 = ctx->getRef(); //get another list reference.
                                          //The 'At' overload is optional for last chain future
std::list<int> listValue = ctx->get(); //get list value

Chaining with the new V2 api:

using namespace Bloomberg::quantum;

// Create a dispatcher
Dispatcher dispatcher;

auto ctx = dispatcher.postFirst([](VoidContextPtr ctx)->int {
    return 55; //Set the 1st value
})->then([](VoidContextPtr ctx)->double {
    // Get the first value and add something to it
    return ctx->getPrev<int>() + 22.33; //Set the 2nd value
})->then([](VoidContextPtr ctx)->std::string {
    return "Hello world!"; //Set the 3rd value
})->finally([](VoidContextPtr ctx)->std::list<int> {
    return {1,2,3}; //Set 4th value
})->end();

Building and installing

Quantum is a header-only library and as such no targets need to be built. To install simply run:

> cmake -Bbuild <options> .
> cd build
> make install

CMake options

Various CMake options can be used to configure the output:

  • QUANTUM_BUILD_DOC : Build Doxygen documentation. Default OFF.
  • QUANTUM_ENABLE_DOT : Enable generation of DOT viewer files. Default OFF.
  • QUANTUM_VERBOSE_MAKEFILE : Enable verbose cmake output. Default ON.
  • QUANTUM_ENABLE_TESTS : Builds the tests target. Default OFF.
  • QUANTUM_BOOST_STATIC_LIBS: Link with Boost static libraries. Default ON.
  • QUANTUM_BOOST_USE_MULTITHREADED : Use Boost multi-threaded libraries. Default ON.
  • QUANTUM_USE_DEFAULT_ALLOCATOR : Use default system supplied allocator instead of Quantum’s. Default OFF.
  • QUANTUM_ALLOCATE_POOL_FROM_HEAP : Pre-allocates object pools from heap instead of the application stack. Default OFF.
  • QUANTUM_BOOST_USE_SEGMENTED_STACKS : Use Boost segmented stacks for coroutines. Default OFF.
  • QUANTUM_BOOST_USE_PROTECTED_STACKS : Use Boost protected stacks for coroutines (slow!). Default OFF.
  • QUANTUM_BOOST_USE_FIXEDSIZE_STACKS : Use Boost fixed size stacks for coroutines. Default OFF.
  • QUANTUM_INSTALL_ROOT : Specify custom install path. Default is /usr/local/include for Linux or c:/Program Files for Windows.
  • QUANTUM_PKGCONFIG_DIR : Specify custom install path for the quantum.pc file. Default is ${QUANTUM_INSTALL_ROOT}/share/pkgconfig. To specify a relative path from QUANTUM_INSTALL_ROOT, omit leading /.
  • QUANTUM_EXPORT_PKGCONFIG : Generate quantum.pc file. Default ON.
  • QUANTUM_CMAKE_CONFIG_DIR : Specify a different install directory for the project’s config, target and version files. Default is ${QUANTUM_INSTALL_ROOT}/share/cmake.
  • QUANTUM_EXPORT_CMAKE_CONFIG : Generate CMake config, target and version files. Default ON.
  • BOOST_ROOT : Specify a different Boost install directory.
  • GTEST_ROOT : Specify a different GTest install directory.

Note: options must be preceded with -D when passed as arguments to CMake.

Running tests

Run the following from the top directory:

> cmake -Bbuild -DQUANTUM_ENABLE_TESTS=ON <options> .
> cd build
> make quantum_test && ctest

Using

To use the library simply include <quantum/quantum.h> in your application. Also, the following libraries must be included in the link:

  • boost_context
  • pthread

Quantum library is fully is compatible with C++11, C++14 and C++17 language features. See compiler options below for more details.

Compiler options

The following compiler options can be set when building your application:

  • __QUANTUM_PRINT_DEBUG : Prints debug and error information to stdout and stderr respectively.
  • __QUANTUM_USE_DEFAULT_ALLOCATOR : Disable pool allocation for internal objects (other than coroutine stacks) and use default system allocators instead.
  • __QUANTUM_ALLOCATE_POOL_FROM_HEAP : Pre-allocates object pools from heap instead of the application stack (default). This affects internal object allocations other than coroutines. Coroutine pools are always heap-allocated due to their size.
  • __QUANTUM_BOOST_USE_SEGMENTED_STACKS : Uses boost segmented stack for on-demand coroutine stack growth. Note that Boost.Context library must be built with property segmented-stacks=on and applying BOOST_USE_UCONTEXT and BOOST_USE_SEGMENTED_STACKS at b2/bjam command line.
  • __QUANTUM_BOOST_USE_PROTECTED_STACKS : Uses boost protected stack for runtime bound-checking. When using this option, coroutine creation (but not runtime efficiency) becomes more expensive.
  • __QUANTUM_BOOST_USE_FIXEDSIZE_STACKS : Uses boost fixed size stack. This defaults to system default allocator.

Application-wide settings

Various application-wide settings can be configured via ThreadTraits, AllocatorTraits and StackTraits.

Documentation

Please see the wiki page for a detailed overview of this library, use-case scenarios and examples.

For class description visit the API reference page.

评论

暂无评论