- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在尝试使用“RayW 手牌评估器”方法来获得纸牌组合分数(7 张牌中最好的 5 张牌)。但是,我在使用此方法时遇到了一些性能问题。根据消息来源 - 使用这种方法,每秒必须可以评估超过 3 亿手牌!我的结果是 1.5 秒内完成 10 次研磨,这要慢很多倍。
“RayW 手部评估器”背后的想法如下:
The Two Plus Two evaluator consists of a large lookup table containing some thirty-two million entries (32,487,834 to be precise). In order to lookup a given 7-card poker hand, you trace a path through this table, performing one lookup per card. When you get to the last card, the value so obtained is the official equivalence value of the hand
代码如下:
namespace eval
{
public struct TPTEvaluator
{
public static int[] _lut;
public static unsafe void Init() // to load a table
{
_lut = new int[32487834];
FileInfo lutFileInfo = new FileInfo("HandRanks.dat");
if (!lutFileInfo.Exists)
{throw new Exception("Handranks.dat not found");}
FileStream lutFile = new FileStream("HandRanks.dat", FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096);
byte[] tempBuffer = new byte[32487834 * 4];
lutFile.Read(tempBuffer, 0, 32487834 * 4);
fixed (int* pLut = _lut)
{ Marshal.Copy(tempBuffer, 0, (IntPtr)pLut, 32487834 * 4);}
tempBuffer = null;
}
public unsafe static int LookupHand(int[] cards) // to get a hand strength
{
fixed (int* pLut = _lut)
{
int p = pLut[53 + cards[0]];
p = pLut[p + cards[1]];
p = pLut[p + cards[2]];
p = pLut[p + cards[3]];
p = pLut[p + cards[4]];
p = pLut[p + cards[5]];
return pLut[p + cards[6]];
}
}
}
这就是我测试这种方法的方式:
private void button4_Click(object sender, EventArgs e)
{
int[] str = new int[] { 52, 34, 25, 18, 1, 37, 22 };
int r1 = 0;
DateTime now = DateTime.Now;
for (int i = 0; i < 10000000; i++) // 10 mil iterations 1.5 - 2 sec
{ r1 = TPTEvaluator.LookupHand(str);} // here
TimeSpan s1 = DateTime.Now - now;
textBox14.Text = "" + s1.TotalMilliseconds;
}
我相信此方法最初是在 C++ 中实现的,但 C# 端口应该运行得更快。 有什么方法可以让我在一秒内接近至少 1 亿手?
到目前为止我尝试了什么:
尝试使用字典查找而不是数组
public void ArrToDict(int[] arr, Dictionary<int, int> dic)
{
for (int i = 0; i < arr.Length; i++)
{
dic.Add(i, arr[i]);
}
}
public unsafe static int LookupHandDict(int[] cards)
{
int p = dict[53 + cards[0]];
p = dict[p + cards[1]];
p = dict[p + cards[2]];
p = dict[p + cards[3]];
p = dict[p + cards[4]];
p = dict[p + cards[5]];
return dict[p + cards[6]];
}
10 手的运行时间几乎慢了 6 倍..
据一位人士称 - 他通过删除“不安全”代码将性能提高了 200 倍。我尝试做同样的事情,但结果几乎相同。
public static int LookupHand(int[] cards)
{
int p = _lut[53 + cards[0]];
p = _lut[p + cards[1]];
p = _lut[p + cards[2]];
p = _lut[p + cards[3]];
p = _lut[p + cards[4]];
p = _lut[p + cards[5]];
return _lut[p + cards[6]];
}
引用如下:
After removing the "unsafe" code parts and some small adjustments in the c# version it is now also around 310 mio.
还有其他方法可以提高手牌排名系统的性能吗?
最佳答案
首先 - 基准测试总是很棘手。在你的机器上以一种方式执行的事情在其他机器上并不总是以相同的方式执行,并且有很多“隐藏”的事情可以使数据无效(比如操作系统甚至硬件完成的缓存)。
话虽如此 - 我只看了一下您的 Init() 方法,但它让我摸不着头脑。我发现很难理解。我使用“不安全”的经验法则是不要使用它,除非我绝对必须。这个 Init() 方法,我假设,被调用一次,对吧?我决定对其进行基准测试:
static void BenchmarkIt(string input, Action myFunc)
{
myWatch.Restart();
myFunc();
myWatch.Stop();
Console.WriteLine(input, myWatch.ElapsedMilliseconds);
}
BenchmarkIt("Updated Init() Method: {0}", Init2);
BenchmarkIt("Original Init() Method: {0}", Init1);
其中 Init1() 是您的原始代码,Init2() 是我重写的代码(为了公平起见,我也多次翻转顺序)。这是我得到的(在我的机器上)...
Updated Init() Method: 110
Original Init() Method: 159
这是我使用的代码。不需要不安全的关键字。
public static void Init2()
{
if (!File.Exists(fileName)) { throw new Exception("Handranks.dat not found"); }
BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open));
try
{
_lut = new int[maxSize];
var tempBuffer = reader.ReadBytes(maxSize * 4);
Buffer.BlockCopy(tempBuffer, 0, _lut, 0, maxSize * 4);
}
finally
{
reader.Close();
}
}
在我看来,这段代码更容易阅读,而且似乎运行得更快。
我知道您可能更关心 LookupHand() 的性能,但我无法做出任何重大改进。我尝试了几种不同的方法,但没有任何帮助。
我能够在 500 毫秒内运行您的代码 100,000,000 次。我在一台相当强大的 64 位笔记本电脑上运行——这似乎是您所期望的速度。正如其他人所说 - 在 Release模式下运行(启用优化)会对性能产生重大影响。
关于c# - 更快的扑克手评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10483993/
在 firefox 中,链接手形光标显示正常,但在 IE7 中显示文本光标。 如何在所有浏览器的链接上获得相同的光标(手)? 我可以在 CSS 重置中添加一些内容,以便在所有浏览器中的链接上获取光标吗
我试图在表单元素上方显示我的表单标签,所以我在我的 CSS 中使用了 display:block。但是,我无法通过这种方式每行显示超过 1 个表单元素。 如何正确更新我的 CSS 以在表单元素上方显示
我想找到人手的宽度,但卡在手上的洞上。 我有一只手的图片并找到了它的二进制文件。手上有一个圆圈,其半径和中心已知(引用对象)。我想找到手的宽度,但它上面有一些补丁(孔),这阻碍了找到手的最佳宽度。 这
我尝试为一款游戏制作一个机器人,但他们有很酷的反像素机器人技术。 所以我想,“如果我可以制作一个机器人,只检查光标是否变为手形然后单击,它就会起作用,”因为我需要收集奖金盒,当你将光标指向它时,它变为
我尝试为一款游戏制作一个机器人,但他们有很酷的反像素机器人技术。 所以我想,“如果我可以制作一个机器人,只检查光标是否变为手形然后单击,它就会起作用,”因为我需要收集奖金盒,当你将光标指向它时,它变为
所以我有一副牌的代码,但我不知道如何让另一个类来处理 4 手牌,每手 10 张牌。另一类应在屏幕上以文字形式打印 4 手 10 张随机卡片。有人可以向我展示如何完成此任务的代码吗?我也使用 blueJ
我正在尝试通过在开放正方形内插入图标来使用 fontawesome 创建图标。悬停时,我想更改正方形内背景的颜色,以及正方形的实际颜色和图标颜色。 我在这里举了一个例子:http://jsfiddle
当我手动启 Action 业时,我正在寻找设置变量的正确方法。 我试过 : stages: - test my_job: stage: test script: - echo "H
我必须添加以下代码: a {cursor:pointer;} 在 angular-ui-bootstrap 中将光标更改为标签、分页、下拉切换等链接上的指针/手。 为什么默认不改为指针?这是故意的吗?
我是一名优秀的程序员,十分优秀!