草庐IT

c# - 在 C++ 中编写性能关键的 C# 代码

coder 2024-02-14 原文

我目前正在处理一些对性能至关重要的代码,我有一个特殊的情况,我喜欢用 C# 编写整个应用程序,但性能原因意味着 C++ 最终要快得多。

我对某些代码的两种不同实现方式(一种使用 C#,另一种使用 C++)进行了一些基准测试,时间显示 C++ 版本快 8 倍,两个版本都处于 Release模式并且启用了所有优化。 (实际上,C# 有被编译为 64 位的优势。我忘记在 C++ 时序中启用它)

所以我想,我可以用 C# 编写大部分代码库(C# 使编写起来非常容易),然后编写性能至关重要的东西的 native 版本。我在 C# 和 C++ 中测试的特定代码片段是花费了 > 95% 的处理时间的关键区域之一。

不过,在这里编写 native 代码的推荐智慧是什么?我从未编写过调用 native C++ 的 C# 应用程序,所以我不知道该怎么做。我想以一种尽可能减少必须进行 native 调用的成本的方式来执行此操作。

谢谢!

编辑:下面是我实际尝试处理的大部分代码。它用于 n 体模拟。 95-99% 的 CPU 时间将花在 Body.Pairwise() 上。

class Body
{
    public double Mass;
    public Vector Position;
    public Vector Velocity;
    public Vector Acceleration;

    // snip

    public void Pairwise(Body b)
    {
        Vector dr = b.Position - this.Position;
        double r2 = dr.LengthSq();
        double r3i = 1 / (r2 * Math.Sqrt(r2));

        Vector da = r3i * dr;
        this.Acceleration += (b.Mass * da);
        b.Acceleration -= (this.Mass * da);
    }

    public void Predict(double dt)
    {
        Velocity += (0.5 * dt) * Acceleration;
        Position += dt * Velocity;
    }

    public void Correct(double dt)
    {
        Velocity += (0.5 * dt) * Acceleration;
        Acceleration.Clear();
    }
}

我还有一个使用以下方法驱动模拟的类:

    public static void Pairwise(Body[] b, int n)
    {
        for (int i = 0; i < n; i++)
            for (int j = i + 1; j < n; j++)
                b[i].Pairwise(b[j]);
    }

    public static void Predict(Body[] b, int n, double dt)
    {
        for (int i = 0; i < n; i++)
            b[i].Predict(dt);
    }

    public static void Correct(Body[] b, int n, double dt)
    {
        for (int i = 0; i < n; i++)
            b[i].Correct(dt);
    }

主循环看起来就像:

for (int s = 0; s < steps; s++)
{
    Predict(bodies, n, dt);
    Pairwise(bodies, n);
    Correct(bodies, n, dt);
}

以上只是我实际正在处理的大型应用程序的最低要求。还有更多的事情正在发生,但是对性能最关键的事情发生在这三个函数中。我知道成对函数很慢(它是 n^2),而且我确实有其他更快的方法(其中一个是 Barnes-hutt,它是 n log n)但这超出了我在此要求的范围问题。

C++ 代码几乎相同:

struct Body
{
public:
    double Mass;
    Vector Position;
    Vector Velocity;
    Vector Acceleration;

    void Pairwise(Body &b)
    {
        Vector dr = b.Position - this->Position;
        double r2 = dr.LengthSq();
        double r3i = 1 / (r2 * sqrt(r2));

        Vector da = r3i * dr;
        this->Acceleration += (b.Mass * da);
        b.Acceleration -= (this->Mass * da);
    }

    void Predict(double dt)
    {
        Velocity += (0.5 * dt) * Acceleration;
        Position += dt * Velocity;
    }

    void Correct(double dt)
    {
        Velocity += (0.5 * dt) * Acceleration;
        Acceleration.Clear();
    }
};

void Pairwise(Body *b, int n)
{
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
            b[i].Pairwise(b[j]);
}

void Predict(Body *b, int n, double dt)
{
    for (int i = 0; i < n; i++)
        b[i].Predict(dt);
}

void Correct(Body *b, int n, double dt)
{
    for (int i = 0; i < n; i++)
        b[i].Correct(dt);
}

主循环:

for (int s = 0; s < steps; s++)
{
    Predict(bodies, n, dt);
    Pairwise(bodies, n);
    Correct(bodies, n, dt);
}

还有一个 Vector 类,它的工作方式就像一个常规的数学 vector ,为简洁起见我不包括在内。

最佳答案

您需要与 native 代码进行交互。你可以把它放在一个 DLL 中并调用。好的,当您不经常转换并且界面很薄时。最灵活和最快速的解决方案是用 C++/CLI 语言编写一个 ref 类包装器。看看this magazine article进行介绍。

最后但同样重要的是,您真的应该分析 C# 代码。 8 倍是相当过分的。在您至少了解它为什么这么慢之前,请不要开始这样做。您不想在 C++ 代码中重现原因,那样会毁掉一周的工作。

谨防错误的直觉。 64 位代码实际上并不更快,它通常比 x86 代码慢一点。它有一堆额外的寄存器,非常不错。但是所有指针的大小都是原来的两倍,而且你不会得到双倍的 cpu 缓存。 .

关于c# - 在 C++ 中编写性能关键的 C# 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5607920/

有关c# - 在 C++ 中编写性能关键的 C# 代码的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

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

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

  5. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  6. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  8. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  9. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  10. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

随机推荐