如何更快地克隆阵列?

发布于 2025-02-04 03:15:08 字数 640 浏览 3 评论 0原文

在国际象棋AI程序中,我有一个必须执行的功能,然后撤消了一步。

为了做到这一点,我需要保存一堆包含有关板上当前情况的数据的备份(例如碎片所在的位置,哪些碎片正在受到攻击,或者如果国王受到检查),然后进行移动,最后重置数组。

我使用的代码是这样的:

int[] piecesBackup = pieces.Clone() as int[];

MakeMove();

pieces = piecesBackup.Clone() as int[];



// I've also tried this



int[] piecesBackup = new int[pieces.Length];
pieces.CopyTo(piecesBackup, 0);

MakeMove();

pieces = new int[piecesBackup.Length];
piecesBackup.CopyTo(pieces, 0);

此代码重复了数千次,并且需要很长时间。我怀疑这里的耗时操作是克隆/复制操作。

我想不出任何更好的方法来解决这个问题:这真的是复制数组内容的唯一方法吗?有什么办法可以简单地使用int [] potebackup = picts然后pipter = potebackup

In a chess AI program, I have a function that has to do and then undo a move.

In order to do this, I need to save a backup of a bunch of arrays containing data about the current situation on the board (like where the pieces are, which pieces are under attack or if a king is in check, for example), then make the move and finally reset the arrays.

The code I'm using is like this:

int[] piecesBackup = pieces.Clone() as int[];

MakeMove();

pieces = piecesBackup.Clone() as int[];



// I've also tried this



int[] piecesBackup = new int[pieces.Length];
pieces.CopyTo(piecesBackup, 0);

MakeMove();

pieces = new int[piecesBackup.Length];
piecesBackup.CopyTo(pieces, 0);

This code is repeated thousands of times and it takes very long to do so. I suspect that the time consuming operation here is the cloning/copying operation.

I can't think of any better approach to this problem: is this really the only way to copy the contents of an array? Is there any way I could simply use int[] piecesBackup = pieces and then pieces = piecesBackup?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

芸娘子的小脾气 2025-02-11 03:15:08

我想您的大部分时间都花在分配物体或清理垃圾中。尽管GC处理大多数方案,但很高的频率分配可能会出现问题。但是,与往常一样,我建议一些分析以确认实际上是这种情况。关于性能,很容易猜测错误。

我会尝试将董事会状态适应不变的结构。由于这是一种值类型,因此您可以完全删除任何堆的分配。

我的国际象棋知识有点生锈,但是如果我没记错的话,随时可以发挥最大的32件作用,每件零件都有64个可能的位置。因此,我们应该能够用32个字节代表整个板,并剩下一些可能的用途。 32字节对于结构而言并不是不合理的大小,应该非常快地创建副本。

使用结构可能需要添加所有32件作为单独的字段,这可能是更多的编写代码,但是优化的代码通常并不漂亮。我还建议您阅读生活的文章对游戏状态优化的一些观点。

I would guess that most of your time is spent allocating objects or cleaning up garbage. While the GC handles most scenarios quite well, very high frequency allocation can be problematic. But as always, I would recommend some profiling to confirm that this is actually the case. It is very easy to guess incorrectly with regards to performance.

I would try to fit the board state within an immutable struct. Since this is a value type you could get away with removing any heap allocations at all.

My chess knowledge is a bit rusty, but if I'm not mistaken there can be max 32 pieces in play at any time, and 64 possible positions for each piece. So we should be able to represent the entire board with 32 bytes, with some bits left over for other possible uses. 32 bytes is not an unreasonable size for a struct, and should be very fast to create a copy of.

Using a struct might require adding all 32 pieces as separate fields, and this might be more code to write, but well optimized code is often not pretty. I would also recommend reading Eric Lipperts articles on Game of Life for some perspective on game state optimization.

清风无影 2025-02-11 03:15:08

考虑每次执行副本时重复使用阵列而不是分配新阵列。分配一个新数组需要时间,然后还有一些垃圾收集将在某个时候出现,以清除不再使用的旧数组。

阵列重复使用是我使用的方法,其性能结果良好。这是示例代码,该代码演示了代码重复使用的时间约为分配新数组的时间。

var stopwatch = new System.Diagnostics.Stopwatch();
var innerIterations = 1000000;

int[] pieces = new int[32];
for (int i = 0; i < pieces.Length; i++)
{
    // Put some fake data in the array
    pieces[i] = i * 3;
}

int[] piecesReuse = new int[32];
for (int i = 0; i < piecesReuse.Length; i++)
{
    // Put some fake data in the array
    piecesReuse[i] = i * 3;
}

for (int j = 0; j < 10; j++)
{ 
    Console.WriteLine($"Begin newAllocate round {j} ...");
    stopwatch.Restart();
    for (int i = 0; i < innerIterations; i++)
    {
        int[] piecesBackup = new int[pieces.Length];
        pieces.CopyTo(piecesBackup, 0);

        MakeMove();

        pieces = new int[piecesBackup.Length];
        piecesBackup.CopyTo(pieces, 0);
    }
    stopwatch.Stop();
    Console.WriteLine($"...completed newAllocate round {j} at {stopwatch.ElapsedMilliseconds} ms.");

    Console.WriteLine($"Begin reusedArray round {j} ...");
    stopwatch.Restart();
    int[] piecesBackupReused = new int[piecesReuse.Length];
    for (int i = 0; i < innerIterations; i++)
    {
        piecesReuse.CopyTo(piecesBackupReused, 0);

        MakeMove();

        piecesBackupReused.CopyTo(piecesReuse, 0);
    }
    stopwatch.Stop();
    Console.WriteLine($"...completed reusedArray round {j} at {stopwatch.ElapsedMilliseconds} ms.");
}

void MakeMove()
{
    // Make some change to the arrays
    pieces[0] = 100;
    piecesReuse[0] = 200;
}

我的机器(YMMV)产生的输出。

Begin newAllocate round 0 ...
...completed newAllocate round 0 at 98 ms.
Begin reusedArray round 0 ...
...completed reusedArray round 0 at 28 ms.
Begin newAllocate round 1 ...
...completed newAllocate round 1 at 54 ms.
Begin reusedArray round 1 ...
...completed reusedArray round 1 at 27 ms.
Begin newAllocate round 2 ...
...completed newAllocate round 2 at 50 ms.
Begin reusedArray round 2 ...
...completed reusedArray round 2 at 27 ms.
Begin newAllocate round 3 ...
...completed newAllocate round 3 at 51 ms.
Begin reusedArray round 3 ...
...completed reusedArray round 3 at 30 ms.
Begin newAllocate round 4 ...
...completed newAllocate round 4 at 50 ms.
Begin reusedArray round 4 ...
...completed reusedArray round 4 at 26 ms.
Begin newAllocate round 5 ...
...completed newAllocate round 5 at 49 ms.
Begin reusedArray round 5 ...
...completed reusedArray round 5 at 24 ms.
Begin newAllocate round 6 ...
...completed newAllocate round 6 at 47 ms.
Begin reusedArray round 6 ...
...completed reusedArray round 6 at 23 ms.
Begin newAllocate round 7 ...
...completed newAllocate round 7 at 45 ms.
Begin reusedArray round 7 ...
...completed reusedArray round 7 at 24 ms.
Begin newAllocate round 8 ...
...completed newAllocate round 8 at 48 ms.
Begin reusedArray round 8 ...
...completed reusedArray round 8 at 32 ms.
Begin newAllocate round 9 ...
...completed newAllocate round 9 at 40 ms.
Begin reusedArray round 9 ...
...completed reusedArray round 9 at 22 ms.

Consider reusing the arrays instead of allocating new arrays each time the copy is performed. Allocating a new array takes time and then there is also the garbage collection that will come along at some point to clear away the old array no longer being used.

Array reuse is an approach that I have used with good performance result. Here is example code to that demonstrates code reuse taking about half as much time as allocating new arrays.

var stopwatch = new System.Diagnostics.Stopwatch();
var innerIterations = 1000000;

int[] pieces = new int[32];
for (int i = 0; i < pieces.Length; i++)
{
    // Put some fake data in the array
    pieces[i] = i * 3;
}

int[] piecesReuse = new int[32];
for (int i = 0; i < piecesReuse.Length; i++)
{
    // Put some fake data in the array
    piecesReuse[i] = i * 3;
}

for (int j = 0; j < 10; j++)
{ 
    Console.WriteLine(
quot;Begin newAllocate round {j} ...");
    stopwatch.Restart();
    for (int i = 0; i < innerIterations; i++)
    {
        int[] piecesBackup = new int[pieces.Length];
        pieces.CopyTo(piecesBackup, 0);

        MakeMove();

        pieces = new int[piecesBackup.Length];
        piecesBackup.CopyTo(pieces, 0);
    }
    stopwatch.Stop();
    Console.WriteLine(
quot;...completed newAllocate round {j} at {stopwatch.ElapsedMilliseconds} ms.");

    Console.WriteLine(
quot;Begin reusedArray round {j} ...");
    stopwatch.Restart();
    int[] piecesBackupReused = new int[piecesReuse.Length];
    for (int i = 0; i < innerIterations; i++)
    {
        piecesReuse.CopyTo(piecesBackupReused, 0);

        MakeMove();

        piecesBackupReused.CopyTo(piecesReuse, 0);
    }
    stopwatch.Stop();
    Console.WriteLine(
quot;...completed reusedArray round {j} at {stopwatch.ElapsedMilliseconds} ms.");
}

void MakeMove()
{
    // Make some change to the arrays
    pieces[0] = 100;
    piecesReuse[0] = 200;
}

The resulting output from my machine (ymmv).

Begin newAllocate round 0 ...
...completed newAllocate round 0 at 98 ms.
Begin reusedArray round 0 ...
...completed reusedArray round 0 at 28 ms.
Begin newAllocate round 1 ...
...completed newAllocate round 1 at 54 ms.
Begin reusedArray round 1 ...
...completed reusedArray round 1 at 27 ms.
Begin newAllocate round 2 ...
...completed newAllocate round 2 at 50 ms.
Begin reusedArray round 2 ...
...completed reusedArray round 2 at 27 ms.
Begin newAllocate round 3 ...
...completed newAllocate round 3 at 51 ms.
Begin reusedArray round 3 ...
...completed reusedArray round 3 at 30 ms.
Begin newAllocate round 4 ...
...completed newAllocate round 4 at 50 ms.
Begin reusedArray round 4 ...
...completed reusedArray round 4 at 26 ms.
Begin newAllocate round 5 ...
...completed newAllocate round 5 at 49 ms.
Begin reusedArray round 5 ...
...completed reusedArray round 5 at 24 ms.
Begin newAllocate round 6 ...
...completed newAllocate round 6 at 47 ms.
Begin reusedArray round 6 ...
...completed reusedArray round 6 at 23 ms.
Begin newAllocate round 7 ...
...completed newAllocate round 7 at 45 ms.
Begin reusedArray round 7 ...
...completed reusedArray round 7 at 24 ms.
Begin newAllocate round 8 ...
...completed newAllocate round 8 at 48 ms.
Begin reusedArray round 8 ...
...completed reusedArray round 8 at 32 ms.
Begin newAllocate round 9 ...
...completed newAllocate round 9 at 40 ms.
Begin reusedArray round 9 ...
...completed reusedArray round 9 at 22 ms.
梅倚清风 2025-02-11 03:15:08

我怀疑这里的耗时操作是克隆/复制操作。

您才是正确的,因为您使用了国际象棋板的天文差代表。您可能需要在国际象棋游戏中存储一系列可以高达40亿个整数的原因?您有64个正方形,总共有32件。

至于您的分析,最多的时间可能是通过分配和释放(GC)所有这些巨大的阵列来获取的时间。您应该使用arraypool&lt;&gt;MemoryPool&lt;&gt;,或者只是具有正确尺寸的普通旧结构。

I suspect that the time consuming operation here is the cloning/copying operation.

You are correct only in that you are using an astronomically bad representation of a chess board. What possible reason would you possibly have to store an array of integers that can go up to 4 billion in a chess game? You have 64 squares and up to 32 pieces total.

As to your analysis, the most time is probably taken by allocating and freeing (GC) all those gigantic arrays. You should be using ArrayPool<> or MemoryPool<> instead, or just plain old structures that have the correct size.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文