免费注册 查看新帖 |

Chinaunix

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

[算法] 多线程读写无锁链表: 之前有没有相同的实现? (更新) [复制链接]

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
21 [报告]
发表于 2017-04-17 10:10 |显示全部楼层
回复 52# wlmqgzm

C++的,不够前沿,性能上也不好说,毕竟wait_free才是最快的

这两句, 前一句就不说什么了; 我不前沿就不前沿呗

第二句你什么意思? 合着我一直说你推崇的那个只能在一个读一个写的情况下可以 wait free; 而我的自己认为多个写 一个读的情况下也是 wait free 压根就没看, 就闭眼睛重复念经了;

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
22 [报告]
发表于 2017-04-18 11:49 |显示全部楼层
本帖最后由 zylthinking 于 2017-04-18 12:04 编辑
wlmqgzm 发表于 2017-04-18 00:52
要么楼主测试一下性能吧,  跟boost lockfree_spsc_queue可以对比测试一下, 那个是wait_free的, 我自己测试 ...

上边是你推崇的 boost , 下边是我的 lkf; 测试的是 1读1写的; 你自己看结果, 单线程下是我的占优势;
多线程下, 发现 boost 这玩意居然不是线程安全的, 我说的线程安全意思是多个读多个写, 它文档中的线程安全很可能是一个读一个写彼此安全 。
多线程下看时间是它占点优势, 但不大; 但考虑到它结果都是错的, 你自己明白是什么意思boost 是 apt install 下载的, 优化选项可能没有优化到最好,  姑且存疑


单线程 boost:
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 17, read 17, nr = 0
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 13, read 13, nr = 0
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 14, read 14, nr = 0
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 12, read 12, nr = 0

单线程 lkf:
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 11, read 11, nr = 0
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 12, read 12, nr = 0
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 11, read 11, nr = 0
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
1 writer, 1 reader, nr = 100000
write 8, read 8, nr = 0



boost多线程 50 读, 2个写, 只看速度, 不管数据正确性

zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 243, read 311
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 248, read 176
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 250, read 305
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 252, read 298
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 246, read 279

boost多线程 500 读, 2个写, 只看速度, 不管数据正确性

zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2404, read 2475
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2477, read 2554
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2520, read 2328
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2474, read 2508
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2508, read 2528



lkf多线程 50 读, 2个写, 只看速度, 最终数据是正确的


zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 305, read 343
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 355, read 356
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 292, read 388
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 323, read 364
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
50 writer, 2 reader, nr = 5000000
write 316, read 373


lkf多线程 500 读, 2个写, 只看速度, 最终数据是正确的

zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2672, read 3622
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2589, read 3986
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2664, read 3614
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2709, read 3611
zylthinking@linux:~$ g++ c.cpp now.c -lpthread; ./a.out
run = 0
500 writer, 2 reader, nr = 50000000
write 2638, read 3717





论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
23 [报告]
发表于 2017-04-18 11:51 |显示全部楼层
zylthinking 发表于 2017-04-18 11:49
上边是你推崇的 boost , 下边是我的 lkf; 测试的是 1读1写的; 你自己看结果, 单线程下是我的占优势;  ...

测试代码:


  1. #include <stdlib.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <pthread.h>
  5. #include <boost/lockfree/spsc_queue.hpp>

  6. #include "lkf.h"
  7. static LKF_LIST(list);

  8. #define nrp 100000
  9. #define nrt 1
  10. uint32_t now();

  11. #define uselkf 0
  12. int nnnnn = 0;
  13. struct lkf_node** node_array = NULL;

  14. static int run = 0;
  15. static struct lkf_node* pool = NULL;
  16. static int indx = 0;
  17. static int nr = 0;
  18. static uint32_t t1;

  19. boost::lockfree::spsc_queue<lkf_node*, boost::lockfree::capacity<50000000> ,
  20.                              boost::lockfree::fixed_sized<true> > spsc_queue;
  21. void* writer(void* any)
  22. {
  23.     int* intp = (int *) any;
  24.     while (run == 0) {
  25.         usleep(100);
  26.     }
  27. #if uselkf
  28.     for (int i = 0; i < nrp; ++i) {
  29.         int idx = __sync_fetch_and_add(&indx, 1);
  30.         struct lkf_node* nodep = &pool[idx];
  31.         lkf_node_put(&list, nodep);
  32.     }
  33. #else
  34.     for (int i = 0; i < nrp; ++i) {
  35.         int idx = __sync_fetch_and_add(&indx, 1);
  36.         struct lkf_node* nodep = &pool[idx];
  37.         spsc_queue.push(nodep);
  38.     }
  39. #endif

  40.     __sync_sub_and_fetch(&run, 1);
  41.     intp[0] = (int) (now() - t1);
  42.     return NULL;
  43. }

  44. void* reader(void* any)
  45. {
  46.     int* intp = (int *) any;
  47.     while (run == 0) {
  48.         usleep(100);
  49.     }
  50. #if uselkf
  51.     while (nr > 0) {
  52.         struct lkf_node* nodep = lkf_node_get(&list);
  53.         if (nodep == NULL) {
  54.             continue;
  55.         }

  56.         __sync_sub_and_fetch(&nr, 1);
  57.         struct lkf_node* cur = NULL;
  58.         do {
  59.             cur = lkf_node_next(nodep);
  60.             if (cur == nodep) {
  61.                 break;
  62.             }

  63.             if (cur != NULL) {
  64.                 __sync_sub_and_fetch(&nr, 1);
  65.             }
  66.         } while (1);
  67.     }
  68. #else
  69.     while (nr > 0) {
  70.         int n = (int) spsc_queue.pop(node_array, nnnnn);
  71.         __sync_sub_and_fetch(&nr, n);
  72.     }
  73. #endif
  74.     intp[0] = (int) (now() - t1);
  75.     return NULL;
  76. }

  77. int main(int argc, char** argv)
  78. {
  79.     int n = 0;
  80.     int used1[1024] = {0};
  81.     for (int i = 0; i < nrt; ++i) {
  82.         pthread_t tid;
  83.         if(0 == pthread_create(&tid, NULL, writer, &used1[i])) {
  84.             ++n;
  85.             pthread_detach(tid);
  86.         }
  87.     }

  88.     nr = nrp * n;
  89. #if !uselkf
  90.     nnnnn = nr;
  91.     node_array = (struct lkf_node**) malloc(sizeof(struct lkf_node*) * nr);
  92.     if (node_array == 0) {
  93.         printf("memory\n");
  94.         return 0;
  95.     }
  96. #endif

  97.     pool = (struct lkf_node *) malloc(sizeof(struct lkf_node) * nr);
  98.     if (pool == 0) {
  99.         printf("memory\n");
  100.         return 0;
  101.     }

  102.     int readers = 0;
  103.     int used2[1024] = {0};
  104.     for (int i = 0; i < 1; ++i) {
  105.         pthread_t tid;
  106.         if(0 == pthread_create(&tid, NULL, reader, &used2[i])) {
  107.             ++readers;
  108.             pthread_detach(tid);
  109.         }
  110.     }

  111.     printf("run = %d\n", run);
  112.     printf("%d writer, %d reader, nr = %d\n", n, readers, nr);

  113.     t1 = now();
  114.     run = n;

  115.     sleep(10);
  116.     int max1 = 0, max2 = 0;
  117.     for (int i = 0; i < n; ++i) {
  118.         if (used1[i] > max1) {
  119.             max1 = used1[i];
  120.         }
  121.     }

  122.     for (int i = 0; i < readers; ++i) {
  123.         if (used2[i] > max2) {
  124.             max2 = used2[i];
  125.         }
  126.     }

  127.     printf("write %d, read %d, nr = %d\n", max1, max2, nr);
  128.     return 0;
  129. }
复制代码

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
24 [报告]
发表于 2017-04-18 11:58 |显示全部楼层
本帖最后由 zylthinking 于 2017-04-18 12:25 编辑
wlmqgzm 发表于 2017-04-18 00:52
要么楼主测试一下性能吧,  跟boost lockfree_spsc_queue可以对比测试一下, 那个是wait_free的, 我自己测试 ...
编辑掉, 不吵架

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
25 [报告]
发表于 2017-04-18 12:30 |显示全部楼层
folklore 发表于 2017-04-18 12:25
第一次听到无锁读***写***队例, 就觉得很神奇。怎么可能无锁呢??????
想啊想, 还是觉得怎么可能无 ...



没有真正无锁的代码; 这前提下, 更多的人在意的是函数的名称中是否有 lock 或 wait 的字眼

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
26 [报告]
发表于 2017-04-18 18:26 |显示全部楼层



真是够扯淡的, 我也送你句: 你还需要继续加强编程能力,加强理论学习
为啥这么说, 下面是理由
1. 你知道你吹牛逼的 boost 里面的 atomic 实现里用了 CAS 吗
2. 你知道 gcc builtin 系列函数本身自带了内存屏障效果吗, 你知道 std::memeory_order 不等于机器指令吗, 你知道相同的指令会以你不知道的面目出现吗?
3. 你知道我用的机器不是你用的机器吗, 你知道看性能对比是看相同外部条件下的表现吗? 你知道你那句话拿到任何地方都会被笑话的吗






论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
27 [报告]
发表于 2017-04-18 18:26 |显示全部楼层
本帖最后由 zylthinking 于 2017-04-18 18:43 编辑


测试代码有问题请指出来, 那一句有问题, 有什么问题
张嘴就说有问题, 却不说什么问题;

就和张嘴就说你的是 lock free, 不是 wait free 一样, 你别把自己太当回事, 没有证据的话, 基本上就是风

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
28 [报告]
发表于 2017-04-18 18:47 |显示全部楼层
本帖最后由 zylthinking 于 2017-04-18 18:55 编辑

回复 69# wlmqgzm

你找找我代码里面的 CAS spin 试试?至于机器差异, 这个好办, 我代码都在这里了, 你要不要在你的机器上跑跑? 或者找出来我在测试代码中哪里偏向我自己的实现了;



论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
29 [报告]
发表于 2017-04-18 18:52 |显示全部楼层
回复 69# wlmqgzm

读毛代码,一看就知道buf[tail].load(std::memory_order_acquire) == invalid_val

buf[tail].load(std::memory_order_acquire) != invalid_val

这两个带着互斥语意的判断就不是单纯机器指令级别的内存屏障能做到的
有异议的话你可以将 load(std::memory_order_acquire)  用汇编指令来实现实现


论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
30 [报告]
发表于 2017-04-18 19:28 |显示全部楼层
回复 73# wlmqgzm

你没有 spin? 我代码还没有完全分析出来, 但似乎味道不对哈, 我继续分析

你只说代码有问题, 就是不撸袖子
我就奇怪了你要我提供测试数据, 我动手就干, 到你头上怎么就只动口呢
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP