C# 固定数组 - 哪种结构读取速度最快?

发布于 2024-11-28 04:19:25 字数 798 浏览 3 评论 0原文

我有一些大型二维数据元素数组。 A 和 B 的尺寸不相等。

A) 介于 5 到 20 之间

B) 介于 1000 到 100000 之间

初始化时间没有问题,因为它只是用于实时应用程序的查找表,因此通过了解值 A 和 B 来索引元素的性能至关重要。当前存储的数据是单个字节值。

我正在考虑这些解决方案:

byte[A][B] datalist1a;

或或

byte[B][A] datalist2a;

byte[A,B] datalist1b;

byte[B,A] datalist2b;

或可能会失去多维,因为我知道固定大小,只需在查找之前将其乘以值即可。

byte[A*Bmax + B] datalist3;

或者

byte[B*Amax + A] datalist4;

我需要知道,当我进行此设置时,应使用哪种数据类型/数组结构来在 C# 中进行最有效的查找。

编辑1 前两个解决方案应该是多维的,而不是多数组。

编辑2 每次查找时都会读取最小维度中的所有数据,但大维度一次仅用于索引一次。

所以它就像 - 从样本 B 中获取所有 A。

I have some large arrays of 2D data elements. A and B aren't equally sized dimensions.

A) is between 5 and 20

B) is between 1000 and 100000

The initialization time is no problem as its only going to be lookup tables for realtime application, so performance on indexing elements from knowing value A and B is crucial. The data stored is currently a single byte-value.

I was thinking around these solutions:

byte[A][B] datalist1a;

or

byte[B][A] datalist2a;

or

byte[A,B] datalist1b;

or

byte[B,A] datalist2b;

or perhaps loosing the multidimension as I know the fixed size and just multiply the to values before looking it up.

byte[A*Bmax + B] datalist3;

or

byte[B*Amax + A] datalist4;

What I need is to know, what datatype/array structure to use for most efficient lookup in C# when I have this setup.

Edit 1
the first two solutions were supposed to be multidimensional, not multi arrays.

Edit 2
All data in the smallest dimension is read at each lookup, but the large one is only used for indexing once at a time.

So its something like - Grab all A's from sample B.

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

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

发布评论

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

评论(5

烟酉 2024-12-05 04:19:25

我敢打赌锯齿状数组,除非 Amax 或 Bmax 是 2 的幂。

我这么说是因为锯齿状数组需要两次索引访问,因此速度非常快。其他形式意味着隐式或显式的乘法。除非乘法是一个简单的移位,否则我认为可能比几次索引访问要重一些。

编辑:这是用于测试的小程序:

class Program
{
    private static int A = 10;
    private static int B = 100;

    private static byte[] _linear;
    private static byte[,] _square;
    private static byte[][] _jagged;



    unsafe static void Main(string[] args)
    {
        //init arrays
        _linear = new byte[A * B];
        _square = new byte[A, B];
        _jagged = new byte[A][];
        for (int i = 0; i < A; i++)
            _jagged[i] = new byte[B];

        //set-up the params
        var sw = new Stopwatch();
        byte b;
        const int N = 100000;

        //one-dim array (buffer)
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _linear[r * B + c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("linear={0}", sw.ElapsedMilliseconds);

        //two-dim array
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _square[r, c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("square={0}", sw.ElapsedMilliseconds);

        //jagged array
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _jagged[r][c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("jagged={0}", sw.ElapsedMilliseconds);

        //one-dim array within unsafe access (and context)
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                fixed (byte* offset = &_linear[r * B])
                {
                    for (int c = 0; c < B; c++)
                    {
                        b = *(byte*)(offset + c);
                    }
                }
            }
        }
        sw.Stop();
        Console.WriteLine("unsafe={0}", sw.ElapsedMilliseconds);

        Console.Write("Press any key...");
        Console.ReadKey();
        Console.WriteLine();
    }
}

I'd bet on the jagged arrays, unless the Amax or Bmax are a power of 2.

I'd say so, because a jagged array needs two indexed accesses, thus very fast. The other forms implies a multiplication, either implicit or explicit. Unless that multiplication is a simple shift, I think could be a bit heavier than a couple of indexed accesses.

EDIT: Here is the small program used for the test:

class Program
{
    private static int A = 10;
    private static int B = 100;

    private static byte[] _linear;
    private static byte[,] _square;
    private static byte[][] _jagged;



    unsafe static void Main(string[] args)
    {
        //init arrays
        _linear = new byte[A * B];
        _square = new byte[A, B];
        _jagged = new byte[A][];
        for (int i = 0; i < A; i++)
            _jagged[i] = new byte[B];

        //set-up the params
        var sw = new Stopwatch();
        byte b;
        const int N = 100000;

        //one-dim array (buffer)
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _linear[r * B + c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("linear={0}", sw.ElapsedMilliseconds);

        //two-dim array
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _square[r, c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("square={0}", sw.ElapsedMilliseconds);

        //jagged array
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _jagged[r][c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("jagged={0}", sw.ElapsedMilliseconds);

        //one-dim array within unsafe access (and context)
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                fixed (byte* offset = &_linear[r * B])
                {
                    for (int c = 0; c < B; c++)
                    {
                        b = *(byte*)(offset + c);
                    }
                }
            }
        }
        sw.Stop();
        Console.WriteLine("unsafe={0}", sw.ElapsedMilliseconds);

        Console.Write("Press any key...");
        Console.ReadKey();
        Console.WriteLine();
    }
}
心在旅行 2024-12-05 04:19:25
  • 多维 ([,]) 数组几乎总是最慢的,除非在严重的随机访问场景下。理论上它们不应该是这样,但这是 CLR 的奇怪之处之一。
  • 锯齿状数组 ([][]) 几乎总是比多维数组快;即使在随机访问场景下。这些都有内存开销。
  • 单维 ([]) 和代数数组 ([y * stride + x]) 是安全代码中随机访问速度最快的。
  • 通常,不安全代码在所有情况下都是最快的(前提是您不重复固定它)。
  • Multidimensional ([,]) arrays are nearly always the slowest, unless under a heavy random access scenario. In theory they shouldn't be, but it's one of the CLR oddities.
  • Jagged arrays ([][]) are nearly always faster than multidimensional arrays; even under random access scenarios. These have a memory overhead.
  • Singledimensional ([]) and algebraic arrays ([y * stride + x]) are the fastest for random access in safe code.
  • Unsafe code is, normally, fastest in all cases (provided you don't pin it repeatedly).
述情 2024-12-05 04:19:25

对于“哪个X更快”(对于所有X)唯一有用的答案是:您必须进行反映您的要求的性能测试。

并记住要考虑,一般*

  • 程序的维护。如果这不是一个快速的解决方案,那么在大多数情况下,稍慢但可维护的程序是更好的选择。
  • 微观基准可能具有欺骗性。例如,仅从集合中读取的紧密循环可能会以在完成实际工作时不可能的方式进行优化。

另外,请考虑您需要查看完整的程序来决定优化哪些地方。将循环速度加快 1% 可能对该循环很有用,但如果它只占完整运行时间的 1%,则不会产生太大差异。


* 但所有规则都有例外。

The only useful answer to "which X is faster" (for all X) is: you have to do performance tests that reflect your requirements.

And remember to consider, in general*:

  • Maintenance of the program. If this is not a quick one off, a slightly slower but maintainable program is a better option in most cases.
  • Micro benchmarks can be deceptive. For instance a tight loop just reading from a collection might be optimised away in ways not possible when real work is being done.

Additionally consider that you need to look at the complete program to decide where to optimise. Speeding up a loop by 1% might be useful for that loop, but if it is only 1% of the complete runtime then it is not making much differences.


* But all rules have exceptions.

自我难过 2024-12-05 04:19:25

在大多数现代计算机上,算术运算比内存查找快得多。
如果您获取不在高速缓存中的内存地址,或者从错误的位置执行无序执行,则您正在查看 10-100 个时钟,则流水线乘法为 1 个时钟。
另一个问题是缓存局部性。
字节[BAmax + A] datalist4;如果您按顺序访问 A 的变化,这似乎是最好的选择。
当访问 datalist4[b
Amax + a] 时,计算机通常会开始拉入 datalist4[bAmax + a+ 64/sizeof(dataListType)], ... +128 ... 等,或者如果检测到反向迭代,则 datalist4[bAmax + a - 64/sizeof(dataListType)]

希望有帮助!

On most modern computers, arithmetic operations are far, far faster than memory lookups.
If you fetch a memory address that isn't in a cache or where the out of order execution pulls from the wrong place you are looking at 10-100 clocks, a pipelined multiply is 1 clock.
The other issue is cache locality.
byte[BAmax + A] datalist4; seems like the best bet if you are accessing with A's varying sequentially.
When datalist4[b
Amax + a] is accessed, the computer will usually start pulling in datalist4[bAmax + a+ 64/sizeof(dataListType)], ... +128 ... etc, or if it detects a reverse iteration, datalist4[bAmax + a - 64/sizeof(dataListType)]

Hope that helps!

你怎么这么可爱啊 2024-12-05 04:19:25

对你来说最好的方法可能是使用 HashMap

字典

May be best way for u will be use HashMap

Dictionary?

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