有人尝试过 C++ 的事务内存吗?
-
01-07-2019 - |
题
我正在查看英特尔的“whatif”网站及其事务内存编译器(每个线程必须进行原子提交或回滚系统内存,就像数据库一样)。
这似乎是一种替代锁和互斥体的有前途的方法,但我找不到很多推荐。这里有人有任何意见吗?
解决方案
我没有使用过英特尔的编译器,但是,Herb Sutter 对此有一些有趣的评论......
您是否发现人们对事务内存有很多兴趣并使用它,或者这个概念对于大多数开发人员来说太难掌握?
目前还无法回答谁在使用它,因为它尚未推向市场。英特尔有一个软件事务内存编译器原型。但是,如果问题是“开发人员太难使用吗?”答案是我当然希望不要。重点是它比锁容易得多。这是研究领域中唯一有望大大减少锁的使用的重大事情。它永远不会完全取代锁,但部分取代锁是我们唯一的希望。
有一些限制。特别是,某些 I/O 本质上不是事务性的 - 您不能采用一个原子块来提示用户输入其姓名并从控制台读取该名称,并且如果它与另一个事务冲突,则自动中止并重试该块;如果您提示两次,用户就能分辨出差异。不过,事务性内存对于只涉及内存的东西来说非常有用。
据我所知,每个主要的硬件和软件供应商都在研发中拥有多种事务内存工具。有关于基本问题的理论答案的会议和学术论文。我们还没有达到可以发货的 T 型车阶段。您可能会看到早期的有限原型,您无法执行无限制的事务内存,例如只能读写 100 个内存位置。不过,这对于启用更多无锁算法仍然非常有用。
其他提示
博士。Dobb's 去年有一篇关于这个概念的文章:Calum Grant 的事务性编程—— http://www.ddj.com/cpp/202802978
它包括使用他的示例库的一些示例、比较和结论。
我基于一些函数式编程思想构建了组合 STM 库。它不需要任何编译器支持(除了它使用 C++17),也不会带来新的语法。一般来说,它采用的接口 STM库 来自哈斯克尔。
所以,我的库有几个不错的属性:
- 单子组合。每个事务都是名为的自定义 monad 内的计算
STML
. 。您可以将单子交易组合成更大的单子交易。 - 事务与数据模型分离。您可以使用事务变量构建并发数据模型(
TVars
)并在其上运行事务。 - 有
retry
组合器。它允许您重新运行事务。对于构建简短且易于理解的交易非常有用。 - 有不同的一元组合器可以快速表达计算。
- 有
Context
. 。每个计算都应该在某个上下文中运行,而不是在全局运行时中运行。因此,如果您需要多个独立的 STM 集群,则可以拥有许多不同的上下文。 - 从概念上讲,实现非常简单。至少, 参考实现 Haskell 中就是如此,但由于缺乏对函数式编程的良好支持,我不得不重新发明几种 C++ 实现方法。
即使我们认为它是实验性的,该库也显示出非常好的稳定性和鲁棒性。此外,我的方法为通过性能、功能、综合性等改进库提供了很多可能性。
为了演示它的工作,我解决了 Dining Philosophers
任务。您可以在下面的链接中找到该代码。交易示例:
STML<bool> takeFork(const TVar<Fork>& tFork)
{
STML<bool> alreadyTaken = withTVar(tFork, isForkTaken);
STML<Unit> takenByUs = modifyTVar(tFork, setForkTaken);
STML<bool> success = sequence(takenByUs, pure(true));
STML<bool> fail = pure(false);
STML<bool> result = ifThenElse(alreadyTaken, fail, success);
return result;
};
更新我写了一个教程,你可以找一下 这里.
在某些情况下,我认为这是有用的,甚至是必要的。
然而,即使处理器具有使该过程更容易的特殊指令,与互斥体或信号量相比,仍然存在很大的开销。根据其实现方式,它也可能会影响实时性能(必须停止中断,或阻止它们写入您的共享区域)。
我的期望是,如果实现了这一点,那么只需要给定内存空间的一部分,因此影响可能是有限的。
-亚当