(Qt с C ++) Чтение файла (~ 25Mb) и сравнение строк оказывается медленнее, чем Python - PullRequest
0 голосов
/ 09 января 2019

У меня есть два файла, «тест» и «образец». каждый файл содержит «rs-числа», за которыми следуют «генотипы».

Файл теста меньше файла примера. только около 150 rs-номеров + их генотипы.

Тем не менее, образец файла содержит более 900k rs-номеров + их генотипы.

readTest () открывает «test.tsv», читает файл построчно и возвращает вектор кортежей. кортеж содержит (rs-номер, тип geno).

analysis () берет результат из readTest (), открывает файл примера, читает файл построчно, а затем выполняет сравнение.

пример из файла примера:

rs12124811 \ t1 \ t776546 \ ТАА \ г \ п

rs11240777 \ t1 \ t798959 \ TGG \ г \ п

rs12124811 и rs11240777 - это числа rs. АА и ГГ являются их генотипами.

время работы будет n * m. В моей версии этой программы на c ++ это занимает 30 секунд, в то время как версия Python занимает всего 15 секунд и 5 секунд с многопроцессорной обработкой.

vector<tuple<QString, QString>> readTest(string test){
// readTest can be done instantly
return tar_gene;
}

// tar_gene is the result from readTest()
// now call analyze. it reads through the sample.txt by line and does 
// comparison.
QString analyze(string sample_name,
         vector<tuple<QString, QString>> tar_gene
         ){

QString data_matches;

QFile file(QString::fromStdString(sample_name));
file.open(QIODevice::ReadOnly);
//skip first 20 lines
for(int i= 0; i < 20; i++){
    file.readLine();
}

while(!file.atEnd()){ // O(m)
    const QByteArray line = file.readLine();
    const QList<QByteArray> tokens = line.split('\t');
    // tar_gene is the result from readTest()
    for (auto i: tar_gene){ // O(n*m)
        // check if two rs-numbers are matched
        if (get<0>(i) == tokens[0]){
            QString i_rs = get<0>(i);
            QString i_geno = get<1>(i);
            QByteArray cur_geno = tokens[3].split('\r')[0];
            // check if their genotypes are matched
            if(cur_geno.length() == 2){
                if (i_geno == cur_geno.at(0) || i_geno == cur_geno.at(1)){
                    data_matches += i_rs + '-' + i_geno + '\n';
                    break; // rs-numbers are unique. we can safely break 
                           // the for loop
                }
            }
            // check if their genotypes are matched
            else if (cur_geno.length() == 1) {
                if (i_geno == cur_geno.at(0)){
                    data_matches += i_rs + '-' + i_geno + '\n';
                    break; // rs-numbers are unique. we can safely break 
                           // the for loop
                }
            }
        }
    }
}
return data_matches; // QString data_matches will be used in main() and 
                     // printed out in text browser
}

Вот полный исходный код

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

QString analyze(string sample_name,
             vector<tuple<QString, QString>> tar_gene,
             int start, int end){

    QString rs_matches, data_matches;

    QFile file(QString::fromStdString(sample_name));
    file.open(QIODevice::ReadOnly);
    //skip first 20 lines
    for(int i= 0; i < 20; i++){
        file.readLine();
    }

    while(!file.atEnd()){
        const QByteArray line = file.readLine();
        const QList<QByteArray> tokens = line.split('\t');
        for (auto i: tar_gene){
            if (get<0>(i) == tokens[0]){
                QString i_rs = get<0>(i);
                QString i_geno = get<1>(i);
                QByteArray cur_geno = tokens[3].split('\r')[0];
                if(cur_geno.length() == 2){
                    if (i_geno == cur_geno.at(0) || i_geno == cur_geno.at(1)){
                        data_matches += i_rs + '-' + i_geno + '\n';
                        break;
                    }
                }
                else if (cur_geno.length() == 1) {
                    if (i_geno == cur_geno.at(0)){
                        data_matches += i_rs + '-' + i_geno + '\n';
                        break;
                    }
                }
            }
        }
    }
    return data_matches;
}

vector<tuple<QString, QString>> readTest(string test){
    vector<tuple<QString, QString>> tar_gene;
    QFile file(QString::fromStdString(test));
    file.open(QIODevice::ReadOnly);
    file.readLine(); // skip first line
    while(!file.atEnd()){
        QString line = file.readLine();
        QStringList templist;
        templist.append(line.split('\t')[20].split('-'));
        tar_gene.push_back(make_tuple(templist.at(0),
                                      templist.at(1)));
    }
    return tar_gene;
}

void MainWindow::on_pushButton_analyze_clicked()
{
    if(ui->comboBox_sample->currentIndex() == 0){
        ui->textBrowser_rs->setText("Select a sample.");
        return;
    }
    if(ui->comboBox_test->currentIndex() == 0){
        ui->textBrowser_rs->setText("Select a test.");
        return;
    }
    string sample = (ui->comboBox_sample->currentText().toStdString()) + ".txt";
    string test = ui->comboBox_test->currentText().toStdString() + ".tsv";

    vector<tuple<QString, QString>> tar_gene;

    QFile file_test(QString::fromStdString(test));
    if (!file_test.exists()) {
        ui->textBrowser_rs->setText("The test file doesn't exist.");
        return;
    }

    tar_gene = readTest(test);

    QFile file_sample(QString::fromStdString(sample));
    if (!file_sample.exists()) {
        ui->textBrowser_rs->setText("The sample file doesn't exist.");
        return;
    }
    clock_t t1,t2;

    t1=clock();
    QString result = analyze(sample, tar_gene, 0, 0);
    t2=clock();
    float diff ((float)t2-(float)t1);

    float seconds = diff / CLOCKS_PER_SEC;
    qDebug() << seconds;
    ui->textBrowser_rsgeno->setText(result);
}

Как мне заставить его работать быстрее? Я переделал свою программу на C ++, потому что ожидал увидеть лучшую производительность, чем версия Python!


с @Felix помогает, моя программа теперь занимает 15 секунд. Я попробую многопоточность позже.

вот примеры файлов с исходными данными:

(test.tsv) rs17760268-С rs10439884-А rs4911642-С rs157640-G ... и больше. Они не отсортированы.

(sample.txt) rs12124811 \ t1 \ t776546 \ ТАА \ г \ п rs11240777 \ t1 \ t798959 \ TGG \ г \ п ... и больше. Они не отсортированы.

есть ли рекомендации по улучшению структуры данных или алгоритмов?

1 Ответ

0 голосов
/ 10 января 2019

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

  1. Убедитесь, что используемый вами Qt был скомпилирован оптимизирован для скорости, а не для размера
  2. Сборка приложения в режиме релиза. Убедитесь, что вы установили правильные флаги компилятора и компоновщика для оптимизации по скорости, а не по размеру
  3. Попробуйте использовать ссылки больше. Это позволяет избежать ненужных операций копирования. Например, используйте for(const auto &i : tar_gene). Есть еще много примеров. По сути, старайтесь избегать всего, что не является ссылкой, насколько это возможно. Это также означает использование std::move и rvalue ссылок, где это возможно.
  4. Включить QStringBuilder. Это оптимизирует объединение строк. Для этого добавьте DEFINES += QT_USE_QSTRINGBUILDER в свой pro-файл.
  5. Используйте либо QString, либо QByteArray для всего кода. Их смешивание означает, что Qt должен конвертировать их каждый раз, когда вы сравниваете их.

Это просто самые простые и простые вещи, которые вы можете сделать. Попробуйте их и посмотрите, сколько скорости вы можете набрать. Если этого по-прежнему недостаточно, вы можете либо попытаться оптимизировать математический алгоритм, который вы здесь реализуете, либо глубже изучить C / C ++, чтобы изучить все небольшие приемы, которые вы можете сделать для большей скорости.

РЕДАКТИРОВАТЬ: Вы также можете попытаться набрать скорость, разделив код на несколько потоков. Делать это вручную - не самая лучшая идея, если вам это интересно, посмотрите QtConcurrent.

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