Эффективное представление данных в структуре данных - PullRequest
0 голосов
/ 25 января 2012

У меня есть следующий формат:

SOLEXA3_1:3:5:1473:616/1    gi|7367913151|ref|NC_007367.1|  100.00  23  0   0   27  49  3404561 3404539 1e-5    46.1
SOLEXA3_1:3:5:1473:616/1    gi|73921565|ref|NC_007367.1|    100.00  23  0   0   27  49  3404561 3404539 1e-5    46.1
SOLEXA3_1:3:5:1474:616/1    gi|32140171|ref|NC_007367.1|    100.00  23  0   0   27  49  3404561 3404539 1e-2    46.1
SOLEXA3_1:3:5:1474:616/1    gi|7354921565|ref|NC_007367.1|  100.00  23  0   0   27  49  3404561 3404539 1e-5    46.1
SOLEXA3_1:3:5:1475:616/1    gi|73921565|ref|NC_007367.1|    100.00  23  0   0   27  49  3404561 3404539 1e-5    46.1
SOLEXA3_1:3:5:1475:616/1    gi|73921565|ref|NC_007367.1|    100.00  23  0   0   27  49  3404561 3404539 1e-5    46.1

По сути, это файл с разделителями табуляции, и у меня будет несколько попаданий для входных данных (первое поле: SOLEXA3_1:3:5:1474:616/1 в качестве примера) и несколько попаданий для конкретного ввода: 32140171 и 7354921565 для вышеупомянутого примера ввода). То, что я хочу сделать, - это создать некое представление в памяти всех обращений для определенного чтения и качества, связанного с каждым обращением, - это предпоследнее поле - 1e-5 и 1e-2 для вышеупомянутых 2 обращений. Итак, я сделал следующее:

У меня есть Map<String, ArrayList<TObjectDoubleMap<String>>>. Где каждая строка в основном является входным идентификатором, а ArrayList состоит из карты из библиотеки Trove , которая содержит пару строк, double - строка, являющаяся идентификатором попадания и счетом. Мой входной файл составляет около 18 миллионов строк, и с кучей -Xmx12g я получаю кучу памяти. Любые идеи, как я могу оптимизировать использование памяти? Имейте в виду, что фактические результаты могут отличаться, поэтому я не думаю, что разделять их можно.

Ответы [ 3 ]

1 голос
/ 25 января 2012

Я думаю, что ваш подход к использованию списка списков в основном хорош, но может быть спроектирован так, чтобы быть более компактным.

Во-первых, убедитесь, что вы канонизируете прочитанные имена. То есть в памяти должен быть только один экземпляр строки с символами «SOLEXA3_1: 3: 5: 1473: 616/1»; используйте карту, чтобы свести имена к каноническому экземпляру перед их использованием.

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

В-третьих, я думаю, что вы можете хранить хиты и их партитуры в очень компактной структуре, если вы готовы выполнить некоторую работу, вручную упаковав оба в длинный (!). Затем вы можете просто хранить отсортированный массив длин для каждого входа.

Вот как я могу канонизировать прочитанные имена:

Map<String, String> names = new HashMap<String, String>();

public String getCanonicalInstanceOfName(String name) {
    String canonicalName = names.get(name);
    if (canonicalName != null) {
        name = canonicalName;
    }
    else {
        names.put(name, name);
    }
    return name;
}

Я могу придумать хотя бы один разумный способ сделать это, но пока это подойдет.

Вот как бы я справился с оценкой попаданий:

public long packHitIDAndScore(String id, float score) {
    long numericID = Long.parseLong(id);
    int scoreAsHundredthsOfAPercent = (int)(score * 100.0);
    long packedIDAndScore = (numericID << 14) + scoreAsHundredthsOfAPercent;
    return packedIDAndScore;
}

14 существует потому, что 14 двоичных битов достаточно велики для хранения значений до 16384, что достаточно для хранения диапазона от 0 до 10000. Обратите внимание, что для идентификатора вы получите только 50 битов, так что было бы полезно проверка, что ни один ID не был больше, чем 1125899906842623.

Поскольку вы используете Trove, вы можете хранить упакованные длинные позиции в TLongArrayList. Сохраните список отсортированным с помощью binarySearch, чтобы найти подходящее место для каждого длинного присоединения к списку, и вставьте, чтобы поместить его туда. Чтобы найти значение в списке, снова используйте binarySearch.

1 голос
/ 25 января 2012

Я бы использовал:

Map<String, ByteArrayOutputStream> map = new HashMap<String, ByteArrayOutputStream>();

Где ключ - это просто объединение двух полей, и вы записываете качество и оценку в ByteArrayOutputStream.

Полученная структура данных будетвыглядеть примерно так:

Key:    "SOLEXA3_1:3:5:1474:616/1_32140171"
Value:  |5|46.1|2|46.1|  //where this is actually just a byte[]

Затем при чтении качеств и оценок вы просто используете readByte () и readDouble (), пока не доберетесь до конца потока.

Конечно, выполнениетаким образом, выполнение запросов становится немного сложнее, но вы значительно сэкономите при распределении памяти.

Пример:

for ( String[] fields : rows ) {
    Map<String, ByteArrayOutputStream> map = new HashMap<String, ByteArrayOutputStream>();
    String key = fields[0] + "_" + fields[1];
    byte quality = Byte.parseByte(fields[10].substring(3));
    double score = Double.parseDouble(fields[11]);

    if ( !map.containsKey(key) ) {
        map.put(key, new ByteArrayOutputStream());
    }
    DataOutputStream dos = new DataOutputStream(map.get(key));
    dos.writeByte(quality);
    dos.writeDouble(score);
}


//reading
for ( String key : map.keySet() ) {
    ByteArrayOutputStream baos = map.get(key);
    int numHits = baos.size()/9; //1 byte for quality, 8 for score
    DataInputStream din = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()));
    System.out.print( key + " - " + numHits);
    while ( din.available() > 0 ) {
        byte quality = din.readByte();
        double score = din.readDouble();
        System.out.print(" (" + quality + ", " + score + ")");
    }
    System.out.print("\n");
}

Используя этот метод, я могу читать и хранить ~ 20 миллионов записей в <1 ГБпамяти.(Примерно через 10 секунд на MacBook Pro). </p>

0 голосов
/ 25 января 2012

Для этого доступно несколько вариантов, но я бы использовал встроенную базу данных (но только если речь не идет о «традиционной» базе данных) - H2 , например.Если вы не будете выполнять очень сложные вычисления для полученных данных, это будет безопасная ставка.

Просто быстрый список других опций:

  • ETL-системы (при условии, что этотребуется база данных)
  • NIO, если нехватка памяти происходит из-за способа чтения файла
  • "Более легкие" структуры данных (попытки ?, FastUtil )

Вы даже можете использовать их комбинацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...