【发布时间】:2011-04-09 21:06:58
【问题描述】:
我目前正在编写一些性能关键代码,并且我有一种特殊情况,我很想用 C# 编写整个应用程序,但性能原因意味着 C++ 最终要快得多。
我对一些代码的两种不同实现(一个在 C# 中,另一个在 C++ 中)进行了一些基准测试,时间显示 C++ 版本快 8 倍,两个版本都处于发布模式并启用了所有优化。 (其实C#有编译成64位的优势。我忘了在C++的时候启用这个)
所以我想,我可以用 C# 编写大部分代码库(C# 使得编写非常容易),然后在性能至关重要的地方编写本机版本。我在 C# 和 C++ 中测试的特定代码段是花费超过 95% 处理时间的关键领域之一。
但是,在这里编写本机代码的推荐智慧是什么?我从未编写过调用本机 C++ 的 C# 应用程序,所以我不知道该怎么做。我希望以一种尽可能减少进行本机调用的成本的方式来做到这一点。
谢谢!
编辑:下面是我实际尝试处理的大部分代码。这是一个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 类,它的工作原理就像一个常规的数学向量,为简洁起见,我不包括在内。
【问题讨论】:
-
C# 不应该更慢。您可以使用未经检查的块来实现更快的代码,以避免溢出检查和其他东西。
-
@Yochai:我已经尝试过,将我所有的算术都包装在不安全的块中。它归结为一些做大量浮点数学的函数,但我每秒都在做大量的计算。另外,我想知道如何从 C# 调用 C++,因为我已经有一些用 C++ 编写的现有代码。我想编写的一些较新的代码与计算无关,但更容易用 C# 编写。
-
@Yochai:我不太确定未检查代码是否比已检查代码快。虽然我承认我无法解释原因,但我目睹了它一直略微变慢的情况。
-
@Mike Bantegui,这是一个老问题,但您应该查看 C++ AMP 以进行 N Body 模拟。通过让它在 GPU 硬件上运行,它可以提供更好的性能提升。应该很简单,因为您已经在调用非托管代码。
标签: c# c++ performance