创建大型二维数组

时间:2012-03-05 17:40:16

标签: c# .net arrays memory memory-management

简单的问题:

如何在C#中使用巨大的二维数组?我想做的是以下几点:

int[] Nodes = new int[1146445];

int[,] Relations = new int[Nodes.Lenght,Nodes.Lenght];

它只是表示我的内存不足错误。

是否有机会在内存中使用如此大的数据? (4GB RAM和6核CPU)^^

我想在二维数组中保存的整数很小。我想从0到1000。

更新:我尝试使用Dictionary<KeyValuePair<int, int>, int>保存关系。它适用于一些添加循环。这是应该创建图表的类。 CreateGraph的实例从xml streamreader获取其数据。

Main(C#backgroundWorker_DoWork)

ReadXML Reader = new ReadXML(tBOpenFile.Text);
CreateGraph Creater = new CreateGraph();

int WordsCount = (int)nUDLimit.Value;
if (nUDLimit.Value == 0) WordsCount = Reader.CountWords();

// word loop
for (int Position = 0; Position < WordsCount; Position++)
{
    // reading and parsing
    Reader.ReadNextWord();

    // add to graph builder
    Creater.AddWord(Reader.CurrentWord, Reader.GetRelations(Reader.CurrentText));
}

string[] Words = Creater.GetWords();
Dictionary<KeyValuePair<int, int>, int> Relations = Creater.GetRelations();

ReadXML的

class ReadXML
{
    private string Path;
    private XmlReader Reader;
    protected int Word;
    public string CurrentWord;
    public string CurrentText;

    public ReadXML(string FilePath)
    {
        Path = FilePath;
        LoadFile();
        Word = 0;
    }

    public int CountWords()
    {
        // caching
        if(Path.Contains("filename") == true) return 1000;

        int Words = 0;
        while (Reader.Read())
        {
            if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "word")
            {
                Words++;
            }
        }

        LoadFile();

        return Words;
    }

    public void ReadNextWord()
    {
        while(Reader.Read())
        {
            if(Reader.NodeType == XmlNodeType.Element & Reader.Name == "word")
            {
                while (Reader.Read())
                {
                    if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "name")
                    {
                        XElement Title = XElement.ReadFrom(Reader) as XElement;
                        CurrentWord = Title.Value;

                        break;
                    }
                }
                while(Reader.Read())
                {
                    if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "rels")
                    {
                        XElement Text = XElement.ReadFrom(Reader) as XElement;
                        CurrentText = Text.Value;

                        break;
                    }
                }
                break;
            }
        }
    }

    public Dictionary<string, int> GetRelations(string Text)
    {
        Dictionary<string, int> Relations = new Dictionary<string,int>();

        string[] RelationStrings = Text.Split(';');

        foreach (string RelationString in RelationStrings)
        {
            string[] SplitString = RelationString.Split(':');

            if (SplitString.Length == 2)
            {
                string RelationName = SplitString[0];
                int RelationWeight = Convert.ToInt32(SplitString[1]);

                Relations.Add(RelationName, RelationWeight);
            }
        }

        return Relations;
    }

    private void LoadFile()
    {
        Reader = XmlReader.Create(Path);
        Reader.MoveToContent();
    }
}

CreateGraph

class CreateGraph
{
    private Dictionary<string, int> CollectedWords = new Dictionary<string, int>();
    private Dictionary<KeyValuePair<int, int>, int> CollectedRelations = new Dictionary<KeyValuePair<int, int>, int>();

    public void AddWord(string Word, Dictionary<string, int> Relations)
    {
        int SourceNode = GetIdCreate(Word);
        foreach (KeyValuePair<string, int> Relation in Relations)
        {
            int TargetNode = GetIdCreate(Relation.Key);
            CollectedRelations.Add(new KeyValuePair<int,int>(SourceNode, TargetNode), Relation.Value);  // here is the error located
        }
    }

    public string[] GetWords()
    {
        string[] Words = new string[CollectedWords.Count];

        foreach (KeyValuePair<string, int> CollectedWord in CollectedWords)
        {
            Words[CollectedWord.Value] = CollectedWord.Key;
        }

        return Words;
    }

    public Dictionary<KeyValuePair<int,int>,int> GetRelations()
    {
        return CollectedRelations;
    }

    private int WordsIndex = 0;
    private int GetIdCreate(string Word)
    {
        if (!CollectedWords.ContainsKey(Word))
        {
            CollectedWords.Add(Word, WordsIndex);
            WordsIndex++;
        }
        return CollectedWords[Word];
    }

}

现在我收到另一个错误:已存在具有相同键的元素。 (在Add课程的CreateGraph

3 个答案:

答案 0 :(得分:2)

当您将Relations设置为锯齿状数组(数组数组)时,您将有更好的机会:

//int[,] Relations = new int[Nodes.Length,Nodes.Length];
int[][] Relations = new int[Nodes.length] [];
for (int i = 0; i < Relations.Length; i++)
    Relations[i] = new int[Nodes.Length];

然后你还需要10k * 10k * sizeof(int)= 400M

哪个应该是可能的,即使以32位运行也是如此。

更新

使用新号码,它是1M * 1M * 4 = 4 TB,这“无法正常工作。” 使用short替换int只能将其降低到2 TB


由于您似乎需要为节点之间的(稀疏)连接分配权重,您应该看看这样的事情是否可行:

struct WeightedRelation 
{ 
   public readonly int node1;
   public readonly int node2;
   public readonly int weight;
}

int[] Nodes = new int[1146445];

List<WeightedRelation> Relations = new List<WeightedRelation>();
Relations.Add(1, 2, 10);
...

这只是基本的想法,你可能需要一个双字典来做快速查找。但是你的记忆大小与实际(非0)关系的数量成正比。

答案 1 :(得分:2)

好的,现在我们知道你真正想要做什么......

int[] Nodes = new int[1146445];

int[,] Relations = new int[Nodes.Length ,Nodes.Length];

您正在尝试分配一个包含1,314,336,138,025个元素的对象,每个元素大小为4个字节。那超过5,000 GB。您对此有何期待?

无论你做什么,你显然会为这么多元素耗尽物理内存......即使CLR让你分配一个这样大小的单个对象。

让我们来看一个50,000的小型示例,最终你需要大约9GB的空间。我不记得当前的限制是什么(这取决于CLR版本号以及你是使用32位还是64位CLR)但我认为它们中的任何一个都不会支持它。

您可以将阵列分解为“行”,如Henk的答案所示 - 这将占用更多内存,但每个阵列都足够小,可以自行处理中的 CLR。它不会帮助你将整个事情融入到记忆中 - 充其量你最终会被遗忘。

您是否可以使用稀疏数组,只为您真正需要访问的元素(或其近似值)分配空间?或者将数据映射到磁盘?如果您给我们更多背景信息,我们可以提出解决方案。

答案 2 :(得分:1)

Jon和Henk提到了稀疏阵列;如果您的许多节点彼此无关,这将非常有用。即使所有节点都与所有其他节点相关,您也可能不需要n×n阵列。

例如,节点可能与自身无关。也许,给定节点x和y,“x与y相关”与“y与x相关”相同。如果这两个都是真的,那么对于4个节点,你只有6个关系,而不是16:

a <-> b
a <-> c
a <-> d
b <-> c
b <-> d
c <-> d

在这种情况下,n-by-n阵列浪费的空间略多于其一半。如果大量节点彼此无关,那么你就浪费了超过一半的空间。

实现此目的的一种快速方法是Dictionary<KeyType, RelationType>,其中密钥唯一地标识两个相关的节点。根据您的确切需求,这可能采取几种不同的形式之一。以下是基于上面定义的节点和关系的示例:

Dictionary<KeyType, Relation> x = new Dictionary<KeyType, RelationType>();
x.Add(new KeyType(a, b), new RelationType(a, b));
x.Add(new KeyType(a, c), new RelationType(a, c));
... etc.

如果关系是自反的,那么KeyType应确保new KeyType(b, a)创建一个与new KeyType(a, b)创建的对象等效的对象。