【问题标题】:(Qt with C++)Reading a file(~25Mb) and comparing strings turn out to be slower than python(Qt with C++)读取文件(~25Mb)并比较字符串比python慢
【发布时间】:2019-01-09 20:49:33
【问题描述】:

我有两个文件,“test”和“sample”。每个文件都包含“rs-numbers”,后跟“genotypes”。

测试文件小于示例文件。只有大约 150 个 rs-numbers+它们的基因型。

但是,示例文件包含超过 900k rs-numbers+它们的基因型。

readTest() 打开“test.tsv”,逐行读取文件,并返回一个元组向量。元组包含(rs 编号,基因类型)。

analyze() 从 readTest() 中获取结果,打开示例文件,逐行读取文件,然后进行比较。

示例文件中的示例:

rs12124811\t1\t776546\tAA\r\n

rs11240777\t1\t798959\tGG\r\n

rs12124811 和 rs11240777 是 rs 编号。 AA和GG是它们的基因型。

运行时间为 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-C rs10439884-A rs4911642-C rs157640-G ... 和更多。它们没有排序。

(示例.txt) rs12124811\t1\t776546\tAA\r\n rs11240777\t1\t798959\tGG\r\n ... 和更多。它们没有排序。

对更好的数据结构或算法有什么建议吗?

【问题讨论】:

  • 您是否在打开优化的情况下进行编译?
  • 为了能够说出可以改进的地方,我们需要查看您的程序,而不仅仅是其中的一部分。
  • @NathanOliver 我该怎么做?我是 qt 和 c++ 的新手。我总是通过在 qt creator 中按“ctrl + r”来运行测试
  • @Slava 对此感到抱歉!更新了!
  • 您应该通过将n*m 替换为更好的东西来开始优化。你可能也可以在 python 中做到这一点。

标签: c++ qt


【解决方案1】:

实际上你可以做很多事情来优化这段代码。

  1. 确保您使用的 Qt 已针对速度而非大小进行了优化
  2. 在发布模式下构建您的应用程序。确保您设置正确的编译器和链接器标志以优化速度,而不是大小
  3. 尝试更多地使用引用。这避免了不必要的复制操作。例如,使用for(const auto &amp;i : tar_gene)。还有很多例子。基本上,尽量避免任何不是参考的东西。这也意味着尽可能使用std::move 和右值引用。
  4. 启用 QStringBuilder。它将优化字符串的连接。为此,请将DEFINES += QT_USE_QSTRINGBUILDER 添加到您的专业文件中。
  5. 对整个代码使用QStringQByteArray。混合它们意味着每次比较它们时 Qt 都必须转换它们。

这些只是您可以做的最基本和最简单的事情。试试它们,看看你能获得多少速度。如果还不够,您可以尝试进一步优化您在此处实现的数学算法,或者深入研究 C/C++ 以学习所有可以提高速度的小技巧。

编辑:您还可以尝试通过在多个线程上拆分代码来提高速度。手动执行此操作不是一个好主意 - 如果您对此感兴趣,请查看 QtConcurrent

【讨论】:

  • 首要的优化是研究数据结构和算法。您提到的主要是微优化。
  • 你是对的 - 优化实际结构可能会产生更大的影响,但不应忽视这些,因为许多小事情可能会产生很大的影响,尤其是当它们经常发生时
  • 天哪。我试过“3”,现在只需要 15 秒!!非常感谢!我会谷歌看看为什么会导致这个问题。
  • 你能解释更多关于 1 和 2 的信息吗?我刚刚用谷歌搜索并找到了这篇文章:link 这篇文章是否与 1&2 相关?
  • 这取决于您使用的编译器。对于 gcc,这些确实是您需要的标志。而3解决的问题是,使用值意味着每个值都是从向量中复制而来的。使用引用而不是复制任何东西。
猜你喜欢
  • 2013-10-14
  • 2022-11-28
  • 1970-01-01
  • 2017-08-20
  • 1970-01-01
  • 1970-01-01
  • 2017-04-23
  • 2014-04-12
  • 1970-01-01
相关资源
最近更新 更多