Python解析stdin的速度比C++快得多

Python parsing stdin much faster than C++

本文关键字:C++ 快得多 速度 解析 stdin Python      更新时间:2023-10-16

我有一个python函数,我希望将其翻译成C++,以尝试获得一些额外的速度(因为它将用于解析>100GB的文件(。我对C++非常缺乏经验,在我的基本翻译之后,我发现我的C++函数运行得慢得多,这让我很震惊。任何关于为什么会这样,或者我可以做些什么来改进我的C++代码的建议都将不胜感激。

脚本概述:该函数从另一个程序读取stdin,检查每一行是否有任何子字符串匹配,并将每一行打印到stdout

Python函数:

def find_tagPy(conditions):
    # conditions e.g. ['TTAT', 'TAT'] etc   
    for line in stdin:
        # Check conditionss against this line
        l = line.split("t")
        if l[0][0] == "@":
            stdout.write(line)
            continue
        FLAG = int(l[1])
        if 1 & FLAG:  # Read has a pair
            for bases in conditions:
                if bases in l[9]:
                    ADD_MATE = 1
                    stdout.write(line)
                    break  # stop looking

C++函数:

void find_tagCpp (vector<string> conditions) {
    cin.sync_with_stdio(false);
    cin.tie(NULL);
    string line;
    while (getline(cin, line)) {
        vector<string> l;
        boost::split(l, line, boost::is_any_of("t"), boost::token_compress_on);
        if (l[0][0] == '@') {
            cout << line << "n";
            continue;
        }
        int FLAG = stoi(l[1]);
        int pair_FLAG = 1;
        if (pair_FLAG & FLAG) {  // Read has a pair
            for (int i=0; i < conditions.size(); i++) {  // If bases in SEQ
                if (l[9].find(conditions[i]) != string::npos) {
                    cout << line << "n";
                    break;  // Stop looking
                }
            }
        }
    }
}

stdin行的一个示例是:

FCC2CCMACXX:4:105:10758:14389#81 chrM 1 32 10S90M=16151 16062 CATCACGATGGATCACAGGTCTATCACCCATTAACCACTCACGGGAGCTTCATGCATTTGGTATTCGTCTGGGGTGCACGCGATAGCATTG bbb^Wcbbbbcbbcbccbba]WQG^bbcdcb_ ^_ c^`ccdddeeeeffeggggiiiiiiiiihiiiiiiiihighiiiihiggeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31

在我的机器上,python函数需要1.97秒,C++函数需要11.05秒(文件大小约为25 mb,但这包括使用上游和下游工具进行处理(

编辑:

我发现了boost::split中的一个瓶颈,这有点令人惊讶:

Python:

for i in range(100000):
    l = line.split("t")

C++:

for (int i=0; i < 100000; i++) {
    vector<string> l;
    boost::split(l, line, boost::is_any_of("t"), boost::token_compress_on);
}

Python=0.0325 s

C++=1.245 s

然而,我的文件只有156980行,所以这不可能是全部问题。

split将片段复制到新的字符串中。这很慢,您不需要它们。相反,在行中搜索你想要的作品的开头(第10个(,然后从那里开始调用find。

我意识到我的原始代码不适合测试,所以我想在这里重构它,并讨论我的发现。我按照建议使用-Oast(最快、积极的优化,Apple LLVM 6.1(打开了编译器优化,相比之下,Python是2.7.10。

Python函数

import time
def fun(line):
    l = line.split(" ", 10)
    if 'TTAGGG' in l[9]:
        pass
line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31"
time0 = time.time()
for i in range(100000):
    fun(line)
print time.time() - time0

C++函数

void fun(string* line, string* substring) {
    vector<string> l;
    boost::split(l, *line, boost::is_any_of(" "));
    if (l[9].find(*substring) != string::npos) {
        // Do nothing
    }
}
int main(int argc, const char * argv[]) {
    string line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31";
    string substring = "TTAGGG";
    boost::timer t;
    for (int i=0; i<100000; i++) {
        fun(&line, &substring);
    }
    cout << t.elapsed() << endl;
    return 0;
}

在我的机器上,我现在将c++函数的时间定为205毫秒,将python函数的时间设为66毫秒。有趣的是,现在几乎整个运行时间都被boost::split函数占用了。

如果我去掉这个函数,使用string.find搜索整行(但不是我想要的(:

if ((*line).find(*substring) != string::npos) {
    // Do nothing
}

c++运行时减少到大约<1毫秒!因此,它似乎起到了推波助澜的作用:分裂是唯一的问题。谢谢你的建议。

使用一些优化尝试此代码

C++函数:

void find_tagCpp (vector<string> conditions) {
    cin.sync_with_stdio(false);
    cin.tie(NULL);
    string line;
    vector<string> l;
    while (getline(cin, line)) {
        l.clear();
        boost::split(l, line, boost::is_any_of("t"), boost::token_compress_on);
        if (l[0][0] == '@') {
            cout << line << "n";
            continue;
        }
        int FLAG = stoi(l[1]);
        int pair_FLAG = 1;
        if (pair_FLAG & FLAG) {  // Read has a pair
            for (int i=0; i < conditions.size(); i++) {  // If bases in SEQ
                if (l[9].find(conditions[i]) != string::npos) {
                    printf("%sn", line.c_str());
                    break;  // Stop looking
                }
            }
        }
    }
}