代码之家  ›  专栏  ›  技术社区  ›  Reed Copsey

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

  •  8
  • Reed Copsey  · 技术社区  · 17 年前

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

    "gotchas" from a programming/porting standpoint . (即:正确处理本机/COM互操作、嵌入结构中的引用类型更改结构的大小等)

    然而 this question and it's answer 让我思考-我还忽略了哪些其他问题?

    有很多问题和博客文章围绕着这个问题,或者说触及了其中的一个方面,但我还没有看到任何一个问题列表。

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

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

    谢谢大家!!

    ----编辑-----

    我意识到早期尝试优化通常是不好的。我知道,事后诸葛亮往往是不好的。我也知道64位的可移植性有它自己的问题——我们每天都在64位系统上运行和测试,以帮助解决这个问题。等

    然而,我的应用程序不是典型的业务应用程序。这是一个科学的软件应用程序。我们有许多进程在所有内核上使用100%的CPU(高度线程化)一次运行数小时。

    我花了很多时间分析应用程序,这会产生巨大的不同。然而,大多数探查器禁用了JIT的许多特性,因此当您在探查器下运行时,内存分配、JIT内联等方面的小细节可能很难确定。因此,我需要提出这个问题。

    8 回复  |  直到 9 年前
        1
  •  4
  •   Eamon Nerbonne    15 年前

    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代码既更长,可读性更低(因为您需要手动内联内容和/或不能使用泛型),又慢得多。

    我已经改用电脑了 Eigen

    Eigen采用模板元编程;in将向量表达式编译为SSE内在指令,并为您执行许多最糟糕的缓存相关循环展开和重新排列;虽然重点是线性代数,但它也适用于整数和非矩阵数组表达式。

    例如,如果 P 是一个矩阵,这类东西只起作用:

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

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

        2
  •  3
  •   Quibblesome    17 年前

    在这种情况下,它优化了临时副本:

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

    将竞争条件放回并导致潜在的空引用异常。

        3
  •  1
  •   Joel Coehoorn    17 年前

    对32位第三方DLL有任何依赖关系。在这种情况下,在64位平台上,它将尝试使用64位约定和结构调用DLL,但它无法工作。

        4
  •  1
  •   Walden Leverich    17 年前

    您提到了移植问题,这些是需要关注的问题。我(显然)不知道您的应用程序,但试图猜测JIT通常完全是浪费时间。编写JIT的人对x86/x64芯片体系结构有着非常深入的了解,并且很可能知道什么性能比这个星球上的任何人都好,什么性能更差。

    刚出现的 应用“那么我就不用担心JIT编译器了。可能有一个愚蠢的循环可以在某个地方避免,这将为您购买100倍的性能改进,您将从尝试二次猜测JIT中获得。让我想起了我们在编写ORM时遇到的问题,我们会查看代码并认为我们可以从中提取一些机器指令。。。当然,代码随后会消失,并通过网络连接到数据库服务器,因此我们将以毫秒为界的进程缩短了几微秒。

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

        5
  •  1
  •   DxCK    16 年前

    关于狡辩者的回答:

    我尝试在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);
            }
        }
    }
    
        6
  •  0
  •   Ivo S    16 年前

    我相信64位JIT并没有完全开发/移植来利用这样的64位体系结构CPU,所以它存在问题,您可能会得到程序集的“模拟”行为,这可能会导致问题和意外行为。我将研究一些可以避免的情况,或者可能会看到是否有好的快速64 C++编译器来编写时间关键计算和算法。但是,即使您很难找到信息,或者没有时间阅读隐藏的代码,我确信在托管代码之外进行大量计算将减少您可能遇到的任何问题&提高性能[在一定程度上确定您已经在这样做了,但请注意:)]

        7
  •  0
  •   Jason Williams    16 年前

    真的吗 “意义重大”,那么您可能无法从代码中挤出更多的速度,应该考虑查看硬件瓶颈(磁盘、RAM或CPU?)并进行升级。(听起来好像您是CPU受限的,所以从这里开始)

    通常,.net和JIT使您免于64位的大多数移植问题。正如您所知,寄存器大小(内存使用变化、封送到本机代码、需要程序的所有部分都是本机64位构建)和一些性能差异(更大的内存映射、更多的寄存器、更宽的总线等)都会产生影响,因此我不能告诉您在这方面您已经知道的任何事情。我看到的其他问题是操作系统,而不是C#问题——例如,64位和WOW64应用程序现在有不同的注册表配置单元,因此必须仔细编写一些注册表访问。

    通常,担心JIT会对代码产生什么影响,并尝试对其进行调整以使其更好地工作是一个坏主意,因为JIT可能会随着.net 4、5或6的变化而变化,而您的“优化”可能会变成低效或更糟的bug。还请记住,JIT专门为其运行的CPU编译代码,因此在开发PC上的改进可能不是在其他PC上的改进。在今天的CPU上使用今天的JIT可能会在几年后升级某些东西时对您造成伤害。

    具体来说,您引用了“属性在x64上没有内联”。当您运行完整个代码库并将所有属性转换为字段时,很可能会出现一个新的用于64位的JIT,该JIT具有内联属性。事实上,它可能比您的“变通”代码表现得更好。让Microsoft为您优化这一点。

    你正确地指出你的记忆模式可以改变。因此,您可能需要更多的RAM、更快的虚拟内存磁盘和更大的CPU缓存。所有硬件问题。您可以通过使用(例如)Int32而不是int来减少影响,但这可能没有多大区别,并且可能会损害性能(因为您的CPU处理本机64位值的效率可能比处理一半大小的32位值更高)。

    您说“启动时间可以更长”,但这在您所说的为其运行的应用程序中似乎无关紧要 小时

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

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

        8
  •  -1
  •   Powerlord    17 年前

    我不太熟悉64位问题,但我有一点意见:

    我们应该忘记小问题 效率,比如说大约97%的 时间:过早优化是关键 万恶之源。