【问题标题】:How to prevent optimization on a class field in C#如何防止对 C# 中的类字段进行优化
【发布时间】:2012-05-04 19:01:46
【问题描述】:

我已经构建了一个抽象类,用于处理我们产品的命令行选项。

只需要创建一个从 AbstractOptions 继承的类,用修饰字段填充它,然后调用继承的 Parse(args) 方法,通过反射自动填充来自命令行的值。在命令行中找不到的值保留其当前(默认)值。

然后,应用程序只需要检查选项字段来获取它们的值。 AbstractOptions 类提供了更多功能,如帮助输出等,但它不是重点。

简短示例:

public class SignalOptions: AbstractOptions
{
    [Option("-i, --iterations", "Number of iterations (0 = infinite).")]
    volatile public int NumberOfIterations;

    [Option("-s, --silent", "Silent mode, only display final results.")]
    volatile public bool Silent;

    [Option("-w, --zwindow", "Window size for z-score analysis.")]
    volatile public int ZWindow = 7;

    [Option("-a, --zalert", "z-score value to consider as peak.")]
    public double ZAlert = 2.1;
}

static void Main(string[] args)
{
    var opts = new SignalOptions();
    opts.Parse(args)

    // If optimizations are turned off, SILENT will be written or not 
    // followind presence or absence of the --silent switch on the command line.
    // If optimizations are turned on, SILENT will never be written.
    // The reflection part is working fine. I suspect the problem is that
    // the compiler of the jitter having never found this set anywhere simply inlines
    // the value 'false' inthe if, because when I step on it, it shows me the value as 
    // true or false, but has the same behavior no matter what value opts.Silence has.
    if( opts.Silent )
        Console.Writeline("SILENT");
}       

现在,我遇到的问题是,由于编译器没有找到任何实际更改 SignalOptions 类的值的代码,它只是内联了在代码中使用它们的值。我通过要求类中的所有 'option' 字段都是 volatile 来规避这个问题,因此没有应用优化,并且工作正常,但不幸的是 volatile 关键字在 double 上无效。

我在网上花了很多时间试图找到解决方法,但没有成功。有没有办法阻止对字段的优化或以其他方式欺骗编译器/抖动以为它们是在运行时使用的?

我还希望尽可能减少调用应用程序的责任。

谢谢

【问题讨论】:

  • 我强烈怀疑您误诊了这个问题。请展示一个简短但完整的程序来说明问题。
  • 编译器如何知道是否使用了公共类的公共字段?它可以在不属于当前构建的程序集中使用
  • 另外:[MethodImpl(MethodImplOptions.NoInlining)] 在字段上无效,所以我怀疑这是您的真实代码
  • 我很确定volatile 没有解决您的问题,只是掩盖了它。你显然在opts.Parse 中做错了什么,但没有相关代码,我们无法告诉你是什么。
  • 您的代码(您尚未发布的代码!)几乎是可笑的错误。这就是为什么发布所有相关代码很重要的原因。这样的问题只会浪费大家的时间。

标签: c# optimization


【解决方案1】:

我在这里有一个本地副本,Parse 写成相当不透明:

public void Parse(string[] args)
{    // deliberately opaque, not that it would make any difference
    string fieldName = (new string('S', 1) + "ilentX").Substring(0, 6);
    GetType().GetField(fieldName).SetValue(this, true);
}

它工作正常。我不相信问题是你认为的那样。

【讨论】:

  • 正如我所说,反射工作正常。这不是问题。问题是,尽管现在将反射值设置为预期值,但其余应用程序代码甚至不会读取该值,除非当然禁用了优化。
  • @user611710 您要禁用哪些优化,具体如何?我们需要确切地了解您所看到的......但是!通过检测从未分配过的字段来删除分支是 not AFAIK 所做的事情。我应该说:我在这里使用发布/优化的构建来测试,在调试器内部和外部。
  • 我还没有弄清楚原因,但无法在实际应用程序中重现问题。感谢大家的时间。我不知道如何将问题标记为过时或不好或其他问题,所以我将此答案作为已接受的答案。
【解决方案2】:

这是我的猜测:

Parse 在单独的线程中运行,但由于您的同步存在某种缺陷,这使得其余代码在没有设置值的情况下运行。

这也可以解释为什么您在调试器中看到正确的值。

更新(自以为是):

Parse 在单独的线程中运行非常奇怪,应该被视为设计缺陷。听起来有人在想“反射很慢,让我们把它放在一个单独的线程中”。

【讨论】:

  • 确实,导致此问题成为 heisenbug 的时间问题可能是一个可能的原因 - 如果没有 Parse 就很难说,但是...
  • 好的,也许我没有正确解释自己。 1)只有一个线程,它是一个简单的控制台应用程序。 2)如果我使用 volatile 关键字,在任何情况下都可以正常工作。 3)如果我删除 volatile 关键字,我需要禁用优化才能使其工作 4)我通过取消选中项目构建选项中的优化代码复选框来禁用优化。
  • @user611710 您能否显示一个完整的示例,该示例实际上显示了您所描述的问题?它不需要使用实际的Parse 代码(请参阅我的示例以进行说明)。如果您可以展示一个基本的独立示例来说明这一点,我们将全力以赴。我的猜测是你会努力在一个简单的例子中重现这个,并且逐渐/小心地将这个简单的例子变成你的真实代码将准确地显示 real 问题的位置是。
  • 如果应用程序这么简单,只需将 Parse 的内容发布在这里或 pastebin.org 上
  • @user611710 也请注意,我尝试过有/没有优化,有/没有volatile
猜你喜欢
  • 1970-01-01
  • 2013-08-22
  • 1970-01-01
  • 2011-05-08
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
相关资源
最近更新 更多