6

我需要在 C# WinForms 中连续声明超过 20000 个项目的方阵。我在 32 位和 64 位操作系统中读到了大约 2GB 的 .Net 对象大小限制。因此,据我了解,唯一的答案是使用不安全的代码或使用 C++ 编译器构建的单独库。

对我来说这个问题是值得的,因为 ushort[20000,20000] 小于 2GB,但实际上我什至无法分配 700MB 的内存。我的限制是 650MB,我不明白为什么 - 我有 32 位 WinXP 和 3GB 内存。我尝试使用 Marshal.AllocHGlobal(700<<20) 但它抛出 OutOfMemoryException,GC.GetTotalMemory 在尝试分配内存之前返回 4.5MB。

我发现只有很多人说使用不安全的代码,但我找不到如何在堆中声明 2-dim 数组(任何堆栈都不能保存如此大量的数据)以及如何使用指针使用它的示例。它是 unsafe{} 括号内的纯 C++ 代码吗?

PS。请不要问为什么我需要这么大的数组......但如果你想 - 我需要分析文本(例如书籍)并找到很多索引。所以答案是 - 单词之间的关系矩阵

编辑:有人可以提供一个使用不安全代码中的指针处理矩阵的小例子。我知道在 32 位下不可能分配更多空间,但我花了很多时间在谷歌上搜索这样的例子,发现什么都没有

4

6 回答 6

5

为什么需要一个巨大的二维阵列?例如,您可以使用锯齿状数组来模拟这一点ushort[][]-几乎一样快,而且您不会达到相同的单个对象限制。当然,您仍然需要 buckets-o-RAM,因此隐含 x64 ......

        ushort[][] arr = new ushort[size][];
        for(int i = 0 ; i < size ; i++) {
            arr[i] = new ushort[size];
        }

除此之外 - 你可能想看看稀疏数组、eta 向量和所有那些爵士乐。

于 2010-04-07T15:38:44.970 回答
4

在 32 位 Windows 中您甚至无法接近 2Gb 分配的原因是 CLR 中的数组布局在连续内存中。在 32 位 Windows 中,地址空间非常有限,以至于在进程的虚拟地址空间中找不到 2Gb 漏洞。您的实验表明可用地址空间的最大区域为 650Mb。迁移到 64 位 Windows 至少应该允许您使用完整的 2Gb 分配。

请注意,32 位 Windows 上的虚拟地址空间限制与您计算机中的物理内存量无关,在您的情况下为 3Gb。相反,限制是由 CPU 用于寻址内存地址的位数引起的。毫不奇怪,32 位 Windows 使用 32 位来访问每个内存地址,这提供了 4GB 的总可寻址内存空间。默认情况下,Windows 为自己保留 2Gb,并将 2Gb 分配给当前正在运行的进程,因此您可以看到为什么 CLR 找不到像 2Gb 分配这样的东西。通过一些技巧,您可以更改操作系统/用户分配,以便 Windows 只为自己保留 1Gb,并为正在运行的进程提供 3Gb,这可能会有所帮助。

于 2010-04-07T15:54:30.380 回答
2

我很开心!:) 最近我玩了一个主题问题 - 尝试使用数据库解决它,但发现这种方式远非完美。矩阵 [20000,20000] 被实现为单个表。即使正确设置了索引,仅在我的 PC 上创建超过 4 亿条记录所需的时间也约为 1 小时。这对我来说并不重要。然后我运行算法来处理该矩阵(需要两次才能加入同一张表!),在它工作了半个多小时后,它甚至没有一步。在那之后,我明白唯一的方法是找到一种方法,只在内存中使用这样的矩阵,然后再次回到 C#。

我创建了试点应用程序来测试内存分配过程并确定使用不同结构的分配过程到底在哪里停止。

正如我在第一篇文章中所说,在 32 位 WinXP 下,使用 2-dim 数组仅分配大约650MB是可能的。使用 Win7 和 64 位编译后的结果也很可悲——不到700MB。

我使用了 JAGGED ARRAYS [][] 而不是单个二维数组 [,],您可以在下面看到结果:

在发布模式下编译为 32 位应用程序 - WinXP 32 位 3GB phys。内存。- 1.45GB 在发布模式下编译为 64 位应用程序 - Win7 64 位 2GB 在 VM 下 - 7.5GB

--我用于测试的应用程序来源附在这篇文章中。我在这里找不到如何附加源文件,所以只描述设计部分并将手动代码放在这里。创建 WinForms 应用程序。使用默认名称放置此类控件:1 个按钮、1 个 numericUpDown 和 1 个列表框 在 .cs 文件中添加下一个代码并运行。

private void button1_Click(object sender, EventArgs e)
        {
            //Log(string.Format("Memory used before collection: {0}", GC.GetTotalMemory(false)));
            GC.Collect();
            //Log(string.Format("Memory used after collection: {0}", GC.GetTotalMemory(true)));
            listBox1.Items.Clear();
            if (string.IsNullOrEmpty(numericUpDown1.Text )) {
                Log("Enter integer value");
            }else{
                int val = (int) numericUpDown1.Value;
                Log(TryAllocate(val));
            }
        }

        /// <summary>
        /// Memory Test method
        /// </summary>
        /// <param name="rowLen">in MB</param>
        private IEnumerable<string> TryAllocate(int rowLen) {
            var r = new List<string>();
            r.Add ( string.Format("Allocating using jagged array with overall size (MB) = {0}", ((long)rowLen*rowLen*Marshal.SizeOf(typeof(int))) >> 20) );
            try {
                var ar = new int[rowLen][];
                for (int i = 0; i < ar.Length; i++) {
                    try {
                        ar[i] = new int[rowLen];
                    }
                    catch (Exception e) {
                        r.Add ( string.Format("Unable to allocate memory on step {0}. Allocated {1} MB", i
                            , ((long)rowLen*i*Marshal.SizeOf(typeof(int))) >> 20 ));
                        break;
                    }
                }
                r.Add("Memory was successfully allocated");
            }
            catch (Exception e) {
                r.Add(e.Message + e.StackTrace);
            }
            return r;
        }

        #region Logging

        private void Log(string s) {
            listBox1.Items.Add(s);
        }

        private void Log(IEnumerable<string> s)
        {
            if (s != null) {
                foreach (var ss in s) {
                    listBox1.Items.Add ( ss );
                }
            }
        }

        #endregion

问题为我解决了。伙计们,提前谢谢你们!

于 2010-04-12T09:21:51.323 回答
0

对于 OutOfMemoryException,请阅读此线程(尤其是 nobugz 和 Brian Rasmussen 的回答):
Microsoft Visual C# 2008 Reducing number of loaded dlls

于 2010-04-07T15:54:41.373 回答
0

如果稀疏数组不适用,最好在 C/C++ 中使用与内存映射文件相关的平台 API 进行操作:http ://en.wikipedia.org/wiki/Memory-mapped_file

于 2010-04-07T15:45:30.387 回答
0

如果您解释了您正在尝试做的事情,那么提供帮助会更容易。也许有比一次分配如此大量的内存更好的方法。

重新设计也是这篇精彩博文中的第一选择:

BigArray,绕过 2GB 数组大小限制

本文建议的选项是:

于 2010-04-07T15:49:36.267 回答