免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: wlmqgzm
打印 上一主题 下一主题

[C++] 读性能超过Memcached 65%, 单核也超过redis, 支持日志支持掉电保护,欢迎试用 [复制链接]

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
61 [报告]
发表于 2016-05-26 13:16 |只看该作者
回复 59# wlmqgzm

Boost.lockfree要求T满足
T must have a copy constructor
T must have a trivial assignment operator
T must have a trivial destructor

而POD除上述三条之外还要求
T must have a trivial constructor
T must be StandardLayout

另外“管理内存”这件事和lockfree本身就会有冲突,所以在lockfree队列里你不应该变更对象的所有权,也就是说你不应该把shared_ptr放进去。
你要么用裸指针,要么用放了糖的裸指针

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
62 [报告]
发表于 2016-05-26 15:08 |只看该作者
本帖最后由 wlmqgzm 于 2016-05-26 15:51 编辑

回复 61# windoze

正如你指出的  Boost.lockfree要求T满足......., 很多条件, 那么就是说: 不是任意对象. 很多情况下无法使用Boost.lockfree,
任意对象的Lock_free_queue 更容易满足编程的要求.

早期代码也是考虑不放shared_ptr, 只使用裸指针( 来自shared_ptr.get() ), 简洁优雅, 功能是一样的,代码效率还要高, 性能也更好,
但是实际在使用中, 这个Lock_free_queue的缺点就是生产者必须要保证长时间持有shared_ptr,不能释放shared_ptr, 否则,消费者有可能会处理已经失效的内存空间,造成系统崩溃,
那么高层代码的复杂度和耦合度就都增加了, 高层代码无法实现Fire and forget 发送后就不管了,

考虑了几个常用场合, 觉得这样的实现对编程人员的要求更高, 对高层代码的限制也更多, 线程代码也无法或者很难做到完全隔离, 并且高层代码增加的行数也更多,更容易出现各种问题,
所以, 有理由实现一个更智能化,更自动化的无锁队列, 实质就是将shared_ptr临时保存一下, 在pop以后的恰当时机再释放. 生产者实现Fire and forget 发送后就不管了
pop得到的是保证安全的裸指针, pop也不用关心指针的内存释放问题.
封装以后, 实现 更安全, 更简洁, 更能适应一切场合,一切对象的代码, 对高并发编程带来的好处是很多的, 它克服了无锁队列的限制条件和安全要求.

template<typename T, unsigned int N=WAIT_FREE_SPSC_QUEUE_DEFAULT_SIZE>
class  Wait_free_spsc_queue
{
  public:
     Wait_free_spsc_queue( void );
     bool push( std::shared_ptr<T>  sp_in );
     T*  pop(  void );
     unsigned int size( void );
     bool  empty( void );
     bool  full( void );
     bool is_lock_free( void );
   

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
63 [报告]
发表于 2016-05-26 15:25 |只看该作者
回复 62# wlmqgzm

不管你信不信,又想lockfree又想fire and forget是不可能的事…………

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
64 [报告]
发表于 2016-05-26 15:41 |只看该作者
本帖最后由 wlmqgzm 于 2016-05-26 16:20 编辑

回复 63# windoze

实际上这个就是我前面说的难度比较大, 技术难度高的原因.

其实, 目前我的代码已经实现了, 只剩下内存申请释放这边, 存在小概率的锁,  前期考虑Google TCMalloc出现锁的概率不高, 没有处理外,
当然如果不在意, 不追求那么极致的话, 确实是可以不处理的.

这部分是可以完全填平的坑, 就是利用Lock_free队列有最大队列长度限制,
自制一个memory pool,因为框架里的内存分配使用情况基本是已知的,用memory pool可以(几乎)完全消除掉所有的内存分配和释放。
使用专门malloc, delete对象, 预先按照最大队列长度申请足够的内存, 在线程内处理就可以了, 就是不够优雅, 而且感觉有点过于花费时间在意义不大的事情上, 毕竟最近很多代码急需做.............

其他的坑都绕过去了 ,  其他部分都找到办法绕过去了, 也很简洁, 不超过200行代码


   

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
65 [报告]
发表于 2016-05-26 17:15 |只看该作者
回复 64# wlmqgzm

先好好想想你的“Lockfree队列”到底是拿来干什么的?做跨线程通信的对不?也就是说这个对象是要被跨线程访问的对不?
在这个前提下,你觉得TCMalloc里的那些thread local memory management对这个lockfree队列能有什么卵用?

老老实实去用atomic+slab搞memory pool吧,别再胡思乱想了。

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
66 [报告]
发表于 2016-05-26 22:25 |只看该作者
回复 65# windoze

thread local memory management对这个lockfree队列能有什么卵用?

内部实现确实是生产者线程thread local的内存, 生产者线程拥有并且负责自动在恰当的时机释放shared_ptr,  消费者线程得到的是裸指针(shared_ptr.get()), 处理的也是裸指针对象, 并不需要释放裸指针
具体的实现就是这样, 我都说了具体实现的技术难度比较大, 所以, 理解不了也正常.

应用层代码范例: 这个是一个实现


template<typename T,unsigned int N=WAIT_FREE_SPSC_QUEUE_DEFAULT_SIZE>
class  Wait_free_spsc_queue
{
  public:
     Wait_free_mpsc_queue( unsigned int uint_producer_count );
     bool push( std::shared_ptr<T>  sp_in );  //  注意这里是shared_ptr, 生产者在push的代码中会检查判断, 哪些shared_ptr可以被释放
     T*  pop(  void );   //  注意, 这里是裸指针, 消费者不允许释放裸指针, 消费者只负责处理消息, 不管内存分配的事情
     unsigned int size( void );
     bool  empty( void );     //  消费者调用
     bool  full( unsigned int uint_thread_num );  // 生产者调用
     bool is_lock_free( void );


  消费者使用Lock_free_queue代码
  Block_write_data  *ptr_block_write_data;
  while( !d_wait_free_mpsc_queue.empty() )  {
    ptr_block_write_data = d_wait_free_mpsc_queue.pop();
    if( ptr_block_write_data==NULL )  break;  //  如果是空指针
    .....处理..........
    continue;
    }
   

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
67 [报告]
发表于 2016-05-26 22:45 |只看该作者
回复 66# wlmqgzm

……你都没明白我在说什么……再说清楚一点吧

你的对象是哪个thread创建的?你的对象是哪个thread销毁的?如果这两个thread不是同一个,那么你显然就没办法让TCMalloc用thread local buffer;如果这两个thread是同一个,那么就意味着每次你要分配和释放对象的时候worker thread还要再和mm thread做一次同步,不管这次同步是不是lockfree,都必然要引入一次thread context switch。
话说你用lockfree不就是为了要避免thread context switch么?

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
68 [报告]
发表于 2016-05-26 23:32 |只看该作者
本帖最后由 wlmqgzm 于 2016-05-26 23:54 编辑

回复 67# windoze

是同一个线程负责申请和释放内存, TCMalloc的资料指出在多数情况下同一线程申请小对象内存和释放都是没有锁的, 也不需要跟mm thread打交道, 除非thread local buffer预先准备的资源耗尽, 才使用锁, 才会有同步, 但是 这个概率接近零

以下引用资料:http://www.mincoder.com/article/2602.shtml

ptmalloc在一台2.8GHz的P4机器上执行一次小对象malloc及free大约需要300纳秒,而TCMalloc的版本同样的操作大约只需要50纳秒。
TCMalloc也减少了多线程程序中的锁竞争情况。对于小对象,已经基本上达到了零竞争。

当分配一个小对象时:(1)我们将其大小映射到对应的尺寸中。 (2)查找当前线程的线程缓存中相应的尺寸的内存链表。 (3)如果当前尺寸内存链表非空,那么从链表中移除的第一个对象并返回它。当我们按照这种方式分配时,TCMalloc不需要任何锁。这就可以极大提高分配的速度,因为锁/解锁操作在一个2.8GHz Xeon上大约需要100纳秒的时间。

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
69 [报告]
发表于 2016-05-27 00:15 |只看该作者
回复 68# wlmqgzm

lockfree队列两端分别是producer thread和consumer thread,然后你又有一个专门的memory management thread负责分配和释放,那么producer每次需要创建一个对象的时候是不是要和这个mm thread做一次同步?consumer thread每次需要释放对象的时候是不是也需要和这个mm thread做一次同步?这些同步是不是会导致thread context switch?
然后你自己看看你这个lockfree队列到底发挥了什么作用?

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
70 [报告]
发表于 2016-05-27 09:12 |只看该作者
本帖最后由 wlmqgzm 于 2016-05-27 09:27 编辑

这里没有memory management thread, 如果有的话, 可能是TCMalloc的. 但是前面也说了, TCMalloc多数情况下, 不需要与TCMalloc其他线程交互, 也不需要锁.

还是以简单的单生产者单消费者Wait_free_queue为例子, 这里永远只有2个线程, 代码中不存在第3个线程, 也不会创建第3个线程, 也没有第3个线程, 永远是单生产者线程负责内存的申请和释放, 只有这一个线程与内存申请释放有关, 消费者线程与内存管理毫无关系, 生产者线程 push代码内部申请本次push需要的小内存后, 有检查确认是否需要释放以前申请的内存; 未来第N次push的时候, 生产者线程在push代码内部检查代码判断符合条件, 释放这次申请的内存.  

Lock_free_queue内存管理设计的原则很简单, 就是: 哪个线程申请的内存, 由哪个线程负责释放, 不与其他线程协调.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP