我的32位头痛现在是64位偏头痛?!? (或64位.NET CLR运行时问题)

时间:2009-03-11 15:22:32

标签: c# .net vb.net clr jit

在64位JIT和32位JIT下运行.NET应用程序时,在性能,内存等方面出现了不寻常的意外后果?我对好事感兴趣,但对人们遇到的令人惊讶的糟糕问题更感兴趣。

我正在编写一个新的.NET应用程序,它将部署在32位和64位。关于移植应用程序的问题有很多问题 - 我不关心"gotchas" from a programming/porting standpoint。 (即:正确处理本机/ COM互操作,嵌入在结构中的引用类型,改变结构的大小等)。

然而,this question and it's answer让我思考 - 我还会忽略其他什么问题?

有许多问题和博客文章都围绕着这个问题,或者涉及到它的一个方面,但是我还没有看到任何可以编制出类似问题的内容。

特别是 - 我的应用程序非常受CPU限制并且具有巨大的内存使用模式(因此首先需要64位),以及本质上是图形化的。我关心在64位Windows上运行的CLR或JIT中可能存在的其他隐藏问题(使用.NET 3.5sp1)。

以下是我目前了解的一些问题:

我想知道人们在64位Windows上的JIT中发现了哪些其他具体的问题,以及是否有任何性能变通办法。

谢谢大家!

---- ----- EDIT

只是为了澄清 -

我知道尽早尝试优化通常很糟糕。我知道第二次猜测系统通常很糟糕。我也知道64bit的可移植性有其自身的问题 - 我们每天在64位系统上运行和测试以帮助解决这个问题。等

但是,我的应用程序不是您典型的业务应用程序。这是一个科学的软件应用程序。我们有许多流程可以在所有核心(高度线程化)上使用100%CPU,每次数小时。

我花了很多时间来分析应用程序,这会产生巨大的差异。但是,大多数分析器都会禁用JIT的许多功能,因此当您在分析器下运行时,内存分配,JIT中的内联等小细节可能很难确定。因此我需要这个问题。

8 个答案:

答案 0 :(得分:4)

.NET中一个特别棘手的性能问题与糟糕的JIT有关:

https://connect.microsoft.com/VisualStudio/feedback/details/93858/struct-methods-should-be-inlined?wa=wsignin1.0

基本上,内联和结构在x64上不能很好地协同工作(虽然那个页面暗示内联现在可以工作,但后续的冗余副本没有被消除,这听起来很可疑,因为它有很小的性能差异)

无论如何,在与.NET进行足够长时间的争吵之后,我的解决方案是将C ++用于数字密集型的任何事情。即使在.NET的“好”情况下,你也没有处理结构并使用优化了边界检查的数组,C ++胜过.NET hands down

如果你做的比点产品更复杂,那么图片会很快变得更糟; .NET代码更长+更不易读(因为你需要手动内联和/或不能使用泛型),而且速度要慢得多。

我已经转而在C ++中使用Eigen:它非常棒,导致可读代码和高性能;然后,一个瘦的C ++ / CLI包装器提供了计算引擎和.NET世界之间的粘合剂。

Eigen通过模板元编程工作;将向量表达式编译成SSE内部指令,并为你做很多最恶劣的缓存相关循环展开和重新排列;虽然专注于线性代数,但它也适用于整数和非矩阵数组表达式。

所以,例如,如果P是一个矩阵,那么这种东西就是Just Works:

1.0 /  (P.transpose() * P).diagonal().sum();

...它没有分配P的临时转置变量,也不计算整个矩阵乘积而只计算它所需的字段。

因此,如果您可以在完全信任中运行 - 只需通过C ++ / CLI使用C ++,它就可以运行得更好。

答案 1 :(得分:3)

我记得我经常听到IRC频道的问题。 它在这个实例中优化了临时副本:

EventHandler temp = SomeEvent;
if(temp != null)
{
    temp(this, EventArgs.Empty);
}

重新放入竞争条件并导致潜在的空引用异常。

答案 2 :(得分:1)

大多数情况下,Visual Studio和编译器都能很好地隐藏您的问题。但是,我知道如果将应用程序设置为自动检测平台(x86 vs x64)并且对32位第三方dll具有任何依赖性,则可能出现一个主要问题。在这种情况下,在64位平台上,它将尝试使用64位约定和结构调用dll,它将无法工作。

答案 3 :(得分:1)

您提到了移植问题,这些是需要关注的问题。我(显然)不知道你的应用程序,但试图猜测JIT通常是完全浪费时间。编写JIT的人对x86 / x64芯片架构有着深刻的理解,并且在所有可能的情况下都知道什么表现更好,表现更差,这可能是地球上任何其他人。

是的,您可能有一个不同且独特的角落案例,但如果您“正在编写应用程序”,那么我不会担心JIT编译器。可能会有一个愚蠢的循环可以避免在某个地方为你提供100倍的性能提升,你可以从尝试猜测JIT获得。让我想起我们编写ORM时遇到的问题,我们会查看代码,并认为我们可以调出一些机器指令......当然,代码然后关闭并通过网络连接到数据库服务器因此,我们在一个以毫秒为界限的过程中微调了几秒。

性能调整的通用规则......如果你没有衡量你的表现,你就不会知道你的瓶颈在哪里,你只是想想你知道。你可能错了。

答案 4 :(得分:1)

关于Quibblesome的回答:

我尝试在没有调试器的发布模式下在Windows 7 x64中运行以下代码,并且从未抛出NullReferenceException

using System;
using System.Threading;

namespace EventsMultithreadingTest
{
    public class Program
    {
        private static Action<object> _delegate = new Action<object>(Program_Event);
        public static event Action<object> Event;

        public static void Main(string[] args)
        {
            Thread thread = new Thread(delegate()
                {
                    while (true)
                    {
                        Action<object> ev = Event;

                        if (ev != null)
                        {
                            ev.Invoke(null);
                        }
                    }
                });
            thread.Start();

            while (true)
            {
                Event += _delegate;
                Event -= _delegate;
            }
        }

        static void Program_Event(object obj)
        {
            object.Equals(null, null);
        }
    }
}

答案 5 :(得分:0)

我认为64 JIT没有完全开发/移植以利用这样的64位架构CPU,因此它存在问题,您可能会得到程序集的'模拟'行为,这可能会导致问题和意外行为。我将研究可以避免和/或可能看到是否有良好的快速64 c ++编译器来编写时间关键计算和算法的情况。但即使您在查找信息方面遇到困难或没有时间阅读已拆解的代码,我也非常确定在托管代码之外进行大量计算可以减少您可能遇到的任何问题。提高性能[有点确定你已经这样做但只是提到:)]

答案 6 :(得分:0)

分析器不应显着影响您的计时结果。如果探查器开销确实“重要”那么你可能无法从代码中挤出更多的速度,并且应该考虑查看你的硬件瓶颈(磁盘,内存或CPU?)和升级。 (听起来你是CPU限制的,所以这就是从哪里开始)

通常,.net和JIT可以解决大多数64位移植问题。如您所知,存在与寄存器大小相关的影响(内存使用情况更改,编组到本机代码,需要程序的所有部分都是本机64位版本)以及一些性能差异(更大的内存映射,更多寄存器,更宽的总线)等),所以我不能告诉你任何比你已经知道的更多的东西。我见过的其他问题是操作系统而不是C# - 例如,64位和WOW64应用程序现在有不同的注册表配置单元,因此必须仔细编写一些注册表访问。

担心JIT会对您的代码执行什么操作以及尝试调整它以使其更好地工作通常是一个坏主意,因为JIT可能会随着.net 4或5或6而改变,并且您的“优化”可能会转变低效率,或更糟糕的是,错误。还要记住,JIT专门为其运行的CPU编译代码,因此开发PC的改进可能不会改善其他PC。在今天的CPU上使用今天的JIT,你可能会在升级过程中的几年内咬住你。

具体来说,您引用“x64上没有内联属性”。当您运行整个代码库时,将所有属性转换为字段,可能会有一个新的64位JIT执行内联属性。实际上,它可能比您的“解决方法”代码表现更好。让微软为您优化。

您正确地指出您的内存配置文件可能会发生变化。因此,您可能需要更多RAM,更快的磁盘用于虚拟内存,以及更大的CPU缓存。所有硬件问题。您可以通过使用(例如)Int32而不是int来减少效果,但这可能没有太大的区别并且可能会损害性能(因为您的CPU可能比半尺寸32位值更有效地处理本机64位值)。

你说“启动时间可能会更长”,但这似乎与你说在100%CPU上运行小时的应用程序无关。

那你真正担心的是什么?也许在32位PC上计算代码,然后在64位PC上执行相同的任务。在4小时的运行中有半小时的差异吗?或者仅差3秒?或者64位PC实际上更快?也许你正在寻找不存在的问题的解决方案。

回到通常的,更通用的建议。确定瓶颈的概况和时间。查看您正在应用的算法和数学过程,并尝试用更有效的算法和数学过程来改进/替换它们。检查您的多线程方法是否有助于而不是损害您的性能(即避免等待和锁定)。尝试减少内存分配/释放 - 例如重新使用对象而不是用新对象替换它们。尽量减少使用频繁的函数调用和虚函数。切换到C ++并摆脱.net强加的垃圾收集,边界检查等固有开销。嗯。这些与64位无关,是吗?

答案 7 :(得分:-1)

我对64位问题并不熟悉,但我确实有一个评论:

  

我们应该忘记小事   效率,约占97%   时间:过早优化是   万恶之源。    - 唐纳德克努特