- 博客(48)
- 收藏
- 关注
原创 rpc:测试std::mutex 和 futex封装的FastPthreadMutex以及butex
FastPthreadMutex在是对futex的封装,在保证互斥的条件下使得线程间切换次数更少,以提高系统性能。
2024-04-17 13:20:51
849
原创 brpc:RemoteTaskQueue
存储非worker线程所创建的 bthreads 的队列(非worker指的是没有建立TaskGroup的线程),非worker线程会随机选择一个 TaskGroup 来推送任务,即将该Task推送至每个TaskGroup的RemoteTaskQueue队列中,由于这里的竞争较小,所以采用的是使用锁来保证线程的安全性。
2024-04-15 11:35:17
412
原创 brpc:WorkStealingQueue
taskflow同款的WorkStealingQueue(但是brpc提供的版本不支持动态扩容),push和pop仅可以在本线程内使用,外部线程通过steal来偷取任务。因为该队列通过实现了一个循环队列,capacity一定要是2的次方。设capacity为C,假如循环队列下标i从0开始,不断增加C在之前的定义中,必须满足其容量大小为2的倍数,而设置M = C-1例如:当C = 8时(二进制位1000),M = 7(二进制位0111)
2024-04-11 19:40:08
851
原创 brpc: bthread的bthread_fcontext_t
defined||defineddefineddefined||defineddefineddefineddefined&&defineddefineddefineddefineddefined所谓bthread,或者说一众fiber的本质,就是能绕过系统调用(线程切换),实现执行状态的切换(每个程序,都是一个状态机,cpu就是无情的不断执行指令的机器),而context,就是保存当前执行状态的struct,该空间是在用户态被创建,所以同线程下的context间的切换,一般不涉及系统调用。
2024-04-11 13:53:05
428
原创 brpc: bthread使用
使用bthread并发编程#include <gflags/gflags.h>#include <butil/logging.h>#include <bthread/bthread.h>static void* func(void* args) { std::string* num = static_cast<std::string*>(args); for(int i = 0; i < 5; i++) { LO
2024-04-09 19:04:42
455
原创 并发编程(三):互斥
而当自旋失败,才会请求系统调用,让操作系统帮助,达到自旋的效果(会有各种优化),所以对于用户态程序来说,mutex在多竞争场景下,不会同上面实现的自旋锁那样,有巨大的性能损失。且在用户态实现的自旋锁,是不完备的,因为用户态无法关中断,即,如果lock后,触发了中断,中断处理程序中,也需要lock,可能会导致死锁等各种问题。自旋锁的一个弊端是,除了进入临界区的那个线程,其他处理器上的线程都在空转(自旋),所以争抢的处理器越多,其利用率越低。上述是使用C++提供的标准api,实现的简易自旋锁。
2024-04-04 17:39:30
897
原创 并发编程(一)多线程存在的问题
考虑上述代码,在单线程下,程序执行正常,但是在并发环境中,对sum的++就会出现意想不到的结果,sum的和不是10000000。更近一步,因为sum++ 在汇编层面不是一条指令,那么强制使用一条指令来进行sum++呢?会发现,依旧不会得到正确的sum。当两个线程在一个cpu核中进行调度,上面的代码会得到正确的值,因为asm volatile( “incq %0” : “+m”(sum));只是一条指令,每次对sum的操作都是原子的。但是,
2024-04-03 22:47:03
730
原创 Taskflow:work-stealing-queue(二)
PS: 这种设计的巧妙性在于,除非deque中的任务数小于等于1, 否则本线程中push 和 pop 是不存在多线程竞态条件的,steal 也只会有一个CAS的开销。推入deque的任务要么被同一线程以相反的顺序执行(对于本线程来说相当于一个stack),要么被另一个线程窃取。
2024-04-03 11:21:39
400
原创 Taskflow:work-stealing-queue(一) 内存序
本文主要讲解一下Taskflow中一个重要数据结构:work-stealing-queue的具体设计首先,需要了解C++11中的六种内存序。
2024-04-01 22:54:58
1371
原创 Taskflow应用:波前并行(Wavefront Parallelism)
【代码】Taskflow应用:波前并行(Wavefront Parallelism)
2024-03-31 23:27:11
303
原创 Taskflow:请求-取消与 性能分析
当向Executor(例如tf::Executor::run)提交任务流时,Executor返回一个tf::Future对象,该对象将保存执行结果。tf::Future是来自std::future的派生类。除了std::future的基本方法外,您还可以调用tf::Future::cancel来取消正在运行的任务流的执行。以下示例取消了任务流的提交,该任务流包含1000个任务,每个任务运行一秒钟。当请求取消时,executor将停止调度任务流的其余Task。但已经运行的Task将继续完成。
2024-03-31 21:15:25
468
原创 Taskflow:限制最大并发度( Limit the Maximum Concurrency)
tf::CriticalSection是tf::Semaphore的包装, 当Task添加到Critical Section时,该Task获取并释放Critical Section内部的信号量。此方法tf::CriticalSection::add为添加到Critical Section的每个Task自动调用tf::Task::acquire和tf::Task::release});});});});});
2024-03-31 20:52:09
594
原创 Taskflow:异常处理(Exception Handling)
对于tf::Executor::corun 或者 tf::Runtime::corun,会直接抛出异常,如果tf::Runtime::corun没有捕获异常,它将被传播到其父task。由于tf::Future是从std::future派生的,它继承了C++标准定义的所有异常处理行为。当A抛出异常时,执行者将取消任务流的执行,停止在A之后运行的每个任务。在这种情况下,B不会运行。所有依赖该异常Task的后续任务将无法运行。当Task抛出异常时,执行器将以tf::Future句柄引用的共享状态存储该异常。
2024-03-31 17:06:58
378
原创 Taskflow:优先级任务(Prioritized Tasking)
总共有三种优先级:tf::TaskPriority::HIGH, tf::TaskPriority::NORMAL, 和 tf::TaskPriority::LOW。对于一个并行Task集合(一组零依赖的Task,可并行执行),executor会尝试先调度高优先级的Task执行,默认情况下,Task的优先级都是tf::TaskPriority::HIGH。注意:Task的优先级只是对于executor的一种提示,当存在多个Worker时,不保证每个Task严格按照优先级来调度。
2024-03-31 16:50:31
420
原创 Taskflow:运行时交互(Interact with the Runtime)
Taskflow允许您通过将运行时对象作为任务的参数与调度运行时进行交互。这主要用于设计从Taskflow的现有设施扩展的专用并行算法。
2024-03-31 16:40:07
452
原创 Taskflow:异步任务(Asynchronous Tasking)
tf::Executor 提供了异步执行Task的操作tf::Executor::async,并返回Future,用于保留该函数调用的结果。由分析工具,可以看到,这个嵌套的异步任务分别执行在不同的线程中:从executor创建的异步任务不属于任何任务流。异步任务的生命周期由创建任务的executor自动管理。
2024-03-31 15:55:53
1151
2
原创 Taskflow:组合任务(Composable Tasking)
);组合方法tf::Taskflow::composed_of要求目标定义graph()方法,该方法返回对目标定义的tf::Graph对象的引用。在运行时,执行者将使用与其他任务流相同的窃取工作调度算法在该图表中运行依赖任务。Taskflow利用这个强大的功能来设计高级算法,例如tf::Pipeline。与其他模块任务一样,Taskflow不拥有自定义可组合图形对象的生命周期,但保留与之的软映射。在执行期间,用户应该保持图形对象的可用性。
2024-03-30 23:09:44
468
原创 Taskflow:条件任务(Conditional Tasking)
条件任务评估一组指令,并返回要执行的下一个后续任务的整数索引。对于cond节点后继的索引正确性,需要依靠用户来保证,如果cond返回的索引超过了合法区间,这个executor将不会调度任务任务。
2024-03-30 21:10:36
1290
原创 Taskflow:子流任务(Subflow Tasking)
DAG任务中,有一种常见的场景,一个任务可能在执行期间产生新的任务,然后紧接着执行新任务。之前提到的静态图就没有办法实现这样一个功能了,所以Taskflow提供了另一种流的节点:Subflow,Subflow的API与Taskflow无异,但又可以作为Taskflow的一个节点。
2024-03-30 16:39:03
791
原创 Taskflow: Static Tasking(静态任务)
静态图,由程序自己定义的并行结构,并且无法从正在运行的依赖关系图中产生新Task。
2024-03-30 09:43:57
459
1
原创 第23章 元编程
元编程本质上是在编写程序的过程中创造新的程序,或者说,它涉及编写一段代码,这段代码将在编译阶段被执行,以生成实现真正所需功能的新代码。这里的“反射属性”意味着元编程部件(metaprogramming component)是它为之生成代码的那个程序的一部分,也就是说,元编程创建或修改了原程序本身的某个部分。元编程之所以被推崇,原因与大多数其他编程技术相同,旨在以较少的努力获得更多的功能,其中努力的程度可以用代码量、维护成本等多种指标衡量。
2024-03-29 10:18:47
320
原创 第22章 桥接静态和动态多态性
静态多态(模板)在运行时表现出与非多态代码相同的性能水平,因为它在编译时就已经确定了可以使用的类型集合。这意味着使用模板时,编译器会针对具体的类型参数生成相应的函数或类实例,这样执行效率高,但缺点在于在运行时无法支持未预见到的新类型。换言之,若要在运行时改变处理的类型,则必须重新编译代码。相反,动态多态(通过继承和虚函数)允许单一版本的多态函数能够与编译时未知的具体类型一起工作,这是因为在运行时通过指向基类的指针或引用调用虚函数,可以根据实际对象类型调用对应的派生类实现。
2024-03-28 14:34:59
915
原创 第21章 模板和继承
如果编译器会实现了EBCO,会发现继承基类的派生类和基类具有同样的大小,且继承优化后的空派生类,大小依旧不变。空基类优化(EBCO,Empty Base Class Optimization)是模板库中的一项重要优化手段。空基类优化是指当一个类作为基类且其内部没有任何非静态数据成员或其他需要运行时内存分配的内容时,编译器可以优化其布局,使得多个空基类不会重复占用存储空间。这样做的结果是可以节省内存,特别是当用户定义的类派生自多个这样的空基类时,每个空基类都可以在对象内存布局中占据零字节的位置。
2024-03-28 09:50:01
490
原创 第19章 Traits的实现(三)
实现一个traits,判断一个类型是否能够转换为另一个类型上面的实现还有一些不足,没有考虑到类似数组等类型的匹配问题,细节可以参考std::is_convertible<>的实现。
2024-03-27 19:43:32
309
1
原创 第19章 Traits的实现(二)
在传统的C/C++编程中,函数通常是值函数,它们接收值作为输入参数,并计算得出一个值作为输出结果。然而,C++模板允许我们定义一类特殊的函数,即类型函数,它们接收一个或多个类型作为参数,并根据这些类型生成一个新的类型或常量作为输出结果。模板可以定义与类型相关的行为,这意味着我们可以编写模板来根据不同类型的行为特性提取相关信息。例如,sizeof是一个内置的类型函数,它接受一个类型作为参数,并返回该类型的大小(以字节为单位)的常量值,这就是一个直观的类型函数示例。在C++中,sizeof。
2024-03-26 22:40:30
812
原创 第 19 章 Traits的实现(一)
在C++编程中,特征模板(Trait Templates)是一种设计模式,用来封装类型相关的属性或行为,以便在编译时获取类型信息或者控制模板行为。特征模板有助于简化代码、提高类型安全性以及在模板编程中实现元编程。
2024-03-25 21:04:28
1113
原创 第 18 章 模板的多态性
多态性也是面向对象泛型编程的基础,C++ 中通过类继承和虚函数来支持多态。因为这些机制 (至少部分) 在运行时处理,所以这里讨论动态多态性。通常所说的多态性,指的就是。然而,模板还允许将不同的特定行为与单个泛型表示关联起来,但这种关联通常在编译时进行处理, 称之为。
2024-03-25 16:21:28
621
原创 第11章 通用库
• 模板允许将函数、函数指针、函数对象、函子和 Lambda 作为可调用对象传递。• 使用重载 operator() 定义类时,将其声明为 const(除非调用改变了状态)。• 使用 std::invoke(),可以处理所有可调用对象的代码,包括成员函数。• 使用 decltype(auto) 来完美地转发返回值。• 类型特征是检查类型属性和功能性函数。• 当需要模板中对象的地址时,可以使用 std::addressof()。
2024-03-24 22:41:50
444
原创 第 10 章 基本模板的术语
声明是一种 C++ 构造,在 C++ 作用域中引入或重新引入一个名称。此介绍会包含该名称的部 分类别,但进行有效声明时不需要详细信息。class C;当声明的结构已知时,或对于变量,必须分配存储空间时,声明就变成了定义。对于类类型定 义,必须提供带括号的主体。对于函数定义,这就必须提供 (一般情况下) 用大括号括起来的函数体, 或者必须将函数指定为 =default 或 =delete。对于变量,初始化或缺少 extern 说明符会导致声明变成 定义。
2024-03-24 21:47:31
312
原创 第 9 章 实际使用模板
按照传统的C++开发习惯,非模板代码会将类和其他类型声明放在头文件中(.hpp或.h等),而全局变量和(非内联)函数的定义则放置在单独的实现文件(.cpp)中。这种方式确保了类型在整个项目中可见,并防止链接时出现重复定义的错误。然而,在模板的实际使用中,这种分离声明和定义的做法会导致问题。例如,有一个名为printTypeof的模板函数,它的声明位于头文件myfirst.hpp中,而实现却在单独的myfirst.cpp文件中。
2024-03-24 21:29:22
245
原创 第八章 编译时编程
• 模板提供了在编译时进行计算的能力 (使用递归进行迭代,使用偏特化或三元操作符进行选 择)。• 使用 constexpr 函数,可以将大多数编译时计算替换为,可在编译时上下文中调用的“普通函 数”。• 使用偏特化,可以根据特定的编译时约束,在类模板的不同实现之间进行选择。• 模板只在需要的时候使用,在函数模板声明中的替换不会导致代码无效。这个原则称为SFINAE(替换失败不为过)。• SFINAE只能用于为特定类型和/或约束提供函数模板。
2024-03-24 21:06:25
917
原创 第 7 章 使用值还是引用?
一般情况下,建议,除非有必须要用引用传递的理由。在C++17之前,如果一个类类型没有定义复制构造函数或移动构造函数,那么即使尝试以值传递的方式传递一个临时对象(右值),也会导致编译错误,因为编译器无法找到合适的方式来创建新对象。然而,在C++17中引入了“guaranteed copy elision”(保证拷贝消除)的概念,即使没有显式定义复制或移动构造函数,编译器在某些情况下也能安全地省略临时对象的创建和后续的复制或移动操作。
2024-03-24 11:33:19
628
原创 第六章 移动语义与enable_if<>
• 模板中,通过将参数声明为转发引用 (声明为模板参数名称后跟 && 形成的类型) 并在转发调 用中使用 std::forward<>(),就可以“完美”地转发参数了。• 使用完美转发成员函数模板时,可能会比预定义用于复制或移动对象的特殊成员函数更匹配。• 使用 std::enable_if<>,可以在编译时条件为 false 时禁用函数模板 (当条件确定,将忽略模板)。• 概念允许对函数模板需求使用更直观的语法。
2024-03-23 22:08:30
806
1
原创 第五章 基础技巧
typename 的引入是用来修饰模板类中的标识符是一个类型。没有typename修饰,编译器无法将Subtype 识别为一个类型(C++20 貌似不需要typename进行修饰了?!
2024-03-23 13:36:34
280
1
原创 第四章 可变参数模板
• 通过使用参数包,可以为任意数量、类型的模板参数定义模板。• 要处理参数,需要递归和/或匹配的非变参函数。• 操作符 sizeof… 可为参数包提供的参数数量。• 可变参数模板的一个应用是转发任意数量的类型参数。• 通过使用折叠表达式,可以对参数包中的所有参数使用相应的操作符。
2024-03-22 19:53:51
493
1
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人