Сокращение объема потребляемой памяти приложения C#

Я разрабатываю приложение C#, которое должно обработать приблизительно 4 000 000 английских предложений. Все эти предложения хранятся в дереве. Где каждый узел в дереве является классом, который имеет эти поля:

class TreeNode
{
    protected string word;
    protected Dictionary<string, TreeNode> children;
}

Моя проблема состоит в том, что приложение израсходовало всю RAM (у меня есть 2 ГБ RAM), когда это достигает 2,000,000-го предложения. Таким образом, этому только удается обработать половину предложений, и затем это замедляется решительно.

Что я могу сделать, чтобы попытаться уменьшить объем потребляемой памяти приложения?

Править: Позвольте мне объяснить немного больше своего приложения. Таким образом, у меня есть приблизительно 300 000 английских предложений, и от каждого предложения я генерирую далее sub предложения как это:

Пример: Предложение: Футбол является очень популярным видом спорта Предложения Sub, в которых я нуждаюсь:

  1. Футбол является очень популярным видом спорта
  2. очень популярный вид спорта
  3. очень популярный вид спорта
  4. очень популярный вид спорта
  5. популярный вид спорта
  6. спорт

Каждое предложение хранится в дереве пословно. Так рассматривая пример выше, у меня есть Класс TreeNode с полем слова = "Футбол", и дети перечисляют, имеет TreeNode для слова ",". Ребенок "является" узлом, "a" узел. Ребенок для "a" узла является "самым" узлом. Я должен сохранить предложения пословно, так как я должен быть в состоянии искать все предложения, запускающиеся с Примера: "Футбол".

Так в основном для каждого слова в предложении я создаю новое (подпредложение). И это - причина, я в конечном счете заканчиваю с 4 000 000 различных предложений. Хранить данные в базе данных не является опцией, так как приложение должно работать над целой структурой сразу. И это далее замедлит процесс, если я должен был остаться пишущим все данные к базе данных.

Спасибо

9
задан PB_MLT 2 January 2010 в 10:12
поделиться

7 ответов

Что вы используете в качестве ключа? Откуда вы берете данные? Если это слова (не полные наборы), то мне интересно, много ли у вас дублированных ключей (различные строковые экземпляры с одним и тем же фундаментальным значением), и в этом случае вам может пригодиться реализация локального интернера для повторного использования значений (и пусть переходные копии будут собирать мусор).

public sealed class StringCache {
    private readonly Dictionary<string,string> values
        = new Dictionary<string,string>(StringComparer.Ordinal);
    public string this[string value] {
        get {
            string cached;
            if (!values.TryGetValue(value, out cached)) {
                values.Add(value, value);
                cached = value;
            }
            return cached;
        }
    }
}

Подтвердите это при построении дерева и используйте (когда вы думаете, что значение, скорее всего, будет дублироваться):

StringCache cache = new StringCache(); // re-use this instance while building
                                       // your tree
...
string s = ... // whatever (from reading your input)
s = cache[s];
10
ответ дан 4 December 2019 в 10:32
поделиться

Не могли бы вы сопоставить каждое слово с информацией? Таким образом, у Вас есть одна карта int - строка, содержащая уникальные английские слова, и древовидная структура, содержащая такие предложения:

class TreeNode
{
    protected int word;
    protected Dictionary<int, TreeNode> children;
}

Dictionary<string, int> _AllWords;

Теперь коллекция _AllWords не является оптимальной для поиска слов, основанных на ключе "как есть". Скорее всего, вам нужен нечто вроде многоклавишного списка, в котором вы можете быстро искать как по ключевому слову, так и по его значению. CodeProject содержит статью об этом.

2
ответ дан 4 December 2019 в 10:32
поделиться

Если вам нужна производительность, и вы чувствуете, что вам нужны все слова в памяти, то я бы посоветовал использовать строковый массив, чтобы содержать все слова. Затем хранить все индексы в отсортированном двоичном дереве.

.
2
ответ дан 4 December 2019 в 10:32
поделиться

Некоторые моменты, о которых следует подумать.

  1. Когда вы инициализируете словарь<,>, передайте максимальное количество нужных вам элементов. Это заставит его выделить достаточное количество вёдер при запуске. По умолчанию инициализируется 0 ведрами, что соответствует 3(prime). Как только вы добавите больше элементов, словарь должен заново инициализировать и копировать все элементы в новое большее хранилище. Если программа никогда не простаивает, то GC не будет собирать старые словари.
  2. Вы можете сэкономить место, закодировав строки. Строки будут использовать два байта на символ в памяти. С некоторыми вспомогательными функциями вы можете иметь такой класс:
    class TreeNode
    {
        protected byte[] word;
        protected Dictionary<byte[], TreeNode> children;

        public string Word
        {
            get { return Encoding.UTF8.GetString(word); }
            set { word = Encoding.UTF8.GetBytes(value); }
        }

        public TreeNode GetChildByKey( string key )
        {
            TreeNode node;
            if(children.TryGetValue( Encoding.UTF8.GetBytes(key), out node  ))
            {
                return node;
            }
            return null;
        }
    }

[Редактирование] И я забыл, что вам также нужен новый сравнитель для ключа byte[].

var children = new Dictonary<string,TreeNode>(new ByteArrayComparer);

public class ByteArrayComparer : IEqualityComparer<byte[]>
{
    public bool Equals(byte[] x, byte[] y)
    {
        if (x.Length != y.Length)
            return false;

        for (int i = 0; i < x.Length; i++)
        {
            if (x[i] != y[i])
                return false;
        }

        return true;
    }

    public int GetHashCode(byte[] a)
    {
        return a[0] | (int)a[1] << 8 | (int)a[2] << 16 | (int)a[3] << 24;
    }
}
1
ответ дан 4 December 2019 в 10:32
поделиться

Это может быть чересчур для вашей ситуации, но вы можете хранить ваши узлы в файлах на диске и использовать B-дерево реализацию, чтобы максимизировать производительность ввода-вывода. Это то, что большинство баз данных используют внутри, потому что просто слишком много данных, чтобы хранить их в памяти.

1
ответ дан 4 December 2019 в 10:32
поделиться

Сам тип Dictionary может потреблять много памяти. Рассматривали ли вы использование вместо этого List > ? Общий Список использует намного меньше памяти для каждого экземпляра, чем общий Словарь .

Конечно, ограничение использования списка вместо словаря состоит в том, что вы не получаете автоматического индексирования по строкам. Это был бы очевидный компромисс между временем и пространством. Если списки короткие, это может быть даже быстрее, чем словарь (линейный поиск ~ 10 ключей часто будет быстрее, чем поиск по хеш-таблице). Даже если по крайней мере большинство списков короткие, это все равно может быть большим улучшением (например, если 95% списков содержат 10 или меньше элементов, а остальные 5% содержат максимум, возможно, 100 элементов. ).

Вы даже можете использовать Collection > , который использует даже меньше памяти, чем List .

4
ответ дан 4 December 2019 в 10:32
поделиться

Единственный способ значительно Уменьшить использование памяти - не сохраняя предложения в памяти.

Что вы пытаетесь достичь? Почему вы строите дерево? Если вы что-то подсчитываете, подсчитаете и отбросьте строки в качестве их прочитанного. Если вы строите график (т. Е. Для анализа отношений между предложением и / или слов), попробуйте перечислить предложения и слова, чтобы они могли быть уникальными / ключом этим удостоверением. Вместо этого используйте этот идентификатор в памяти.

Я надеюсь, что это поможет.

0
ответ дан 4 December 2019 в 10:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: