草庐IT

C++0x 内存模型和推测加载/存储

coder 2023-05-30 原文

所以我正在阅读有关即将推出的 C++0x 标准的一部分的内存模型。但是,对于允许编译器执行的一些限制,特别是关于推测性加载和存储的一些限制,我有点困惑。

首先,一些相关的东西:

Hans Boehm's pages about threads and the memory model in C++0x

Boehm, "Threads Cannot be Implemented as a Library"

Boehm and Adve, "Foundations of the C++ Concurrency Memory Model"

Sutter, "Prism: A Principle-Based Sequential Memory Model for Microsoft Native Code Platforms", N2197

Boehm, "Concurrency memory model compiler consequences", N2338

现在,基本思想本质上是“无数据争用程序的顺序一致性”,这似乎是在易于编程和允许编译器和硬件机会进行优化之间的一个不错的折衷。如果不同线程对同一内存位置的两次访问没有排序,则定义为发生数据竞争,其中至少一次存储到内存位置,并且至少一次不是同步操作。这意味着对共享数据的所有读/写访问都必须通过某种同步机制,例如互斥锁或对原子变量的操作(嗯,可以通过宽松的内存顺序对原子变量进行操作仅供专家使用,但默认提供顺序一致性)。

鉴于此,我对普通共享变量上的虚假或推测性加载/存储的限制感到困惑。例如,在 N2338 中我们有示例

switch (y) {
    case 0: x = 17; w = 1; break;
    case 1: x = 17; w = 3; break;
    case 2: w = 9; break;
    case 3: x = 17; w = 1; break;
    case 4: x = 17; w = 3; break;
    case 5: x = 17; w = 9; break;
    default: x = 17; w = 42; break;
}

编译器不允许转换成的

tmp = x; x = 17;
switch (y) {
    case 0: w = 1; break;
    case 1: w = 3; break;
    case 2: x = tmp; w = 9; break;
    case 3: w = 1; break;
    case 4: w = 3; break;
    case 5: w = 9; break;
    default: w = 42; break;
}

因为如果 y == 2 存在对 x 的虚假写入,如果另一个线程同时更新 x,这可能是一个问题。但是,为什么这是一个问题?这是一场数据竞赛,无论如何都是被禁止的;在这种情况下,编译器通过两次写入 x 只会使情况变得更糟,但即使是一次写入也足以进行数据竞争,不是吗? IE。一个合适的 C++0x 程序需要同步对 x 的访问,这样就不会再出现数据争用了,虚假存储也不会成为问题?

我同样对 N2197 中的示例 3.1.3 以及其他一些示例感到困惑,但也许对上述问题的解释也能解释这一点。

编辑:答案:

推测性存储存在问题的原因在于,在上面的 switch 语句示例中,程序员可能选择有条件地获取保护 x 的锁,仅当 y != 2 时。因此,推测性存储可能会引入数据竞争,即原始代码中不存在,因此禁止转换。同样的论点也适用于 N2197 中的示例 3.1.3。

最佳答案

我不熟悉您提到的所有内容,但请注意,在 y==2 的情况下,在代码的第一位,x 根本没有被写入(或读取,就此而言)。在第二段代码中,它被写入了两次。这比只写一次和写两次(至少在现有的线程模型中,例如 pthreads 中)有更大的区别。此外,存储一个根本不会存储的值比仅存储一次与存储两次的区别更大。由于这两个原因,您不希望编译器只是用 tmp = x; 替换无操作。 x = 17; x = tmp;.

假设线程 A 想假设没有其他线程修改 x。希望它被允许期望如果 y 为 2,并且它向 x 写入一个值,然后将其读回,它将取回它已写入的值,这是合理的。但是,如果线程 B 正在同时执行您的第二段代码,那么线程 A 可以写入 x 并稍后读取它,并取回原始值,因为线程 B 在写入“之前”保存并在“之后”恢复它。或者它可以取回 17,因为线程 B 在写入“之后”存储了 17,并且在线程 A 读取之后再次存储了 tmp。线程 A 可以进行它喜欢的任何同步,但这无济于事,因为线程 B 没有同步。它不同步的原因(在 y==2 的情况下)是它没有使用 x。因此,特定代码位是否“使用 x”这一概念对线程模型很重要,这意味着不能允许编译器在“不应该”时更改代码以使用 x。

简而言之,如果允许您提出的转换,引入虚假写入,则永远不可能分析一些代码并得出结论它不会修改 x(或任何其他内存位置)。有许多方便的习惯用法因此是不可能的,例如在没有同步的线程之间共享不可变数据。

所以,虽然我不熟悉 C++0x 对“数据竞争”的定义,但我假设它包含一些允许程序员假设对象未写入的条件,并且这种转换会违反那些条件。我推测如果 y==2,那么你的原始代码,连同并发代码: x = 42; x = 1; z = x 在另一个线程中,未定义为数据竞争。或者至少如果它是一场数据竞争,它不会允许 z 以 17 或 42 结束。

考虑到在这个程序中,y 中的值 2 可能被用来表示,“还有其他线程在运行:不要修改 x,因为我们这里没有同步,所以会引入数据竞争”。也许根本没有同步的原因是,在 y 的所有其他情况下,没有其他线程在运行可以访问 x。在我看来,C++0x 想要支持这样的代码是合理的:

if (single_threaded) {
    x = 17;
} else {
    sendMessageThatSafelySetsXTo(17);
}

很明显,您不希望将其转换为:

tmp = x;
x = 17;
if (!single_threaded) {
    x = tmp;
    sendMessageThatSafelySetsXTo(17);
}

这与您的示例中的转换基本相同,但只有 2 种情况,而不是足以使它看起来像一个很好的代码大小优化。

关于C++0x 内存模型和推测加载/存储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2001913/

有关C++0x 内存模型和推测加载/存储的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  5. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  6. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  7. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  8. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  10. ruby-on-rails - 如何将验证与模型分开 - 2

    我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

随机推荐