我将2D字段的位存储在5个无符号长数组中。
我要争取最好的表现。
我正在C#中工作,但是我尝试通过在C++中实现我的类来设置基准。
这里的问题是,C#实现大约需要10秒才能完成,而C++大约需要1秒才能使其速度比快10倍。 C++是VS2015中的x64构建。 C#在x64 VS2015 .NET 4.6中。当然都是在发布中。
编辑:在稍微优化C#代码后,相对于C++ 1.3秒,它仍然需要7到8秒。
注意:x86中的 C++大约需要6秒钟才能完成。我在64位计算机上运行代码。
问题:是什么使C++更快?有没有一种方法可以优化C#代码,使其至少类似地快? (也许是一些不安全的魔法?)
让我感到困惑的是,我们所谈论的只是遍历数组和按位运算。它不应该与C++差不多吗?
示例代码:
在实现中有两个简单的功能。 Left()和Right()将整个字段左移1位。在多头之间适当地带有一点点。
C++
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
class BitField
{
private:
unsigned long long LEFTMOST_BIT = 0x8000000000000000;
unsigned long long RIGHTMOST_BIT = 1;
public:
unsigned long long Cells_l[5];
BitField()
{
for (size_t i = 0; i < 5; i++)
{
Cells_l[i] = rand(); // Random initialization
}
}
void Left()
{
unsigned long long carry = 0;
unsigned long long nextCarry = 0;
for (int i = 0; i < 5; i++)
{
nextCarry = (Cells_l[i] & LEFTMOST_BIT) >> 63;
Cells_l[i] = Cells_l[i] << 1 | carry;
carry = nextCarry;
}
}
void Right()
{
unsigned long long carry = 0;
unsigned long long nextCarry = 0;
for (int i = 4; i >= 0; i--)
{
nextCarry = (Cells_l[i] & RIGHTMOST_BIT) << 63;
Cells_l[i] = Cells_l[i] >> 1 | carry;
carry = nextCarry;
}
}
};
int main()
{
BitField bf;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
for (int i = 0; i < 100000000; i++)
{
bf.Left();
bf.Left();
bf.Left();
bf.Right();
bf.Right();
bf.Left();
bf.Right();
bf.Right();
}
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(t2 - t1).count();
cout << "Time: " << duration << endl << endl;
// Print to avoid compiler optimizations
for (size_t i = 0; i < 5; i++)
{
cout << bf.Cells_l[i] << endl;
}
return 0;
}
C#
using System;
using System.Diagnostics;
namespace TestCS
{
class BitField
{
const ulong LEFTMOST_BIT = 0x8000000000000000;
const ulong RIGHTMOST_BIT = 1;
static Random rnd = new Random();
ulong[] Cells;
public BitField()
{
Cells = new ulong[5];
for (int i = 0; i < 5; i++)
{
Cells[i] = (ulong)rnd.Next(); // Random initialization
}
}
public void Left()
{
ulong carry = 0;
ulong nextCarry = 0;
for (int i = 0; i < 5; i++)
{
nextCarry = (Cells[i] & LEFTMOST_BIT) >> 63;
Cells[i] = Cells[i] << 1 | carry;
carry = nextCarry;
}
}
public void Right()
{
ulong carry = 0;
ulong nextCarry = 0;
for (int i = 4; i >= 0; i--)
{
nextCarry = (Cells[i] & RIGHTMOST_BIT) << 63;
Cells[i] = Cells[i] >> 1 | carry;
carry = nextCarry;
}
}
}
class Program
{
static void Main(string[] args)
{
BitField bf = new BitField();
Stopwatch sw = new Stopwatch();
// Call to remove the compilation time from measurements
bf.Left();
bf.Right();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
bf.Left();
bf.Left();
bf.Left();
bf.Right();
bf.Right();
bf.Left();
bf.Right();
bf.Right();
}
sw.Stop();
Console.WriteLine($"Done in: {sw.Elapsed.TotalMilliseconds.ToString()}ms");
}
}
}
编辑:修复了示例代码中的“nextCarry”错字。
最佳答案
部分差异可能是由于两个版本之间的代码差异所致-您没有在C++ nextCarry
和C#Left
中都未分配给Right
,但是在示例中,这些可能是拼写错误。
您可能要看一下两者的反汇编以了解不同之处,但是主要是由于C++编译器有更多时间花在优化代码上。在这种情况下,它将展开循环,内联所有函数调用(包括构造函数),并将Cells_l
中的所有内容推入寄存器。因此,存在一个使用寄存器而不访问内存的大循环。
我没有看过C#编译后的输出,但我怀疑它是否可以完成任何工作。
另外,如注释中所述,将C#代码中的所有Cells.Length
调用替换为5(就像C++代码中的调用一样)。