Python 解析标准输入比 C++ 快得多
Python parsing stdin much faster than C++
我有一个 python 函数,我希望将其转换为 C++ 以尝试获得额外的速度(因为它将用于解析 >100GB 的文件)。我对 C++ 非常缺乏经验,并且在我的基本翻译后发现我的 C++ 函数 运行 慢了很多,这让我感到震惊。任何关于为什么会这样,或者我可以做些什么来改进我的 C++ 代码的任何指示都将不胜感激。
脚本概述:该函数从另一个程序读取标准输入,检查每一行是否有任何子字符串匹配,并将每一行打印到标准输出
Python function:
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:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_
c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65RG: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 秒
C++ = 1.245 秒
但是我的文件只有 156,980 行,所以这不是全部问题。
拆分将片段复制到新的字符串中。这很慢,您不需要它们。而是在行中搜索您想要的片段的开头(第 10 个),然后从那里开始调用查找。
我意识到我的原始代码不适合测试,所以我想我会在这里重构它,并讨论我的发现。
我按照建议使用 -Ofast(最快,积极的优化,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 毫秒!所以看来 boost::split 是一个问题。感谢您的建议。
尝试对这段代码进行一些优化
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("%s\n", line.c_str());
break; // Stop looking
}
}
}
}
}
我有一个 python 函数,我希望将其转换为 C++ 以尝试获得额外的速度(因为它将用于解析 >100GB 的文件)。我对 C++ 非常缺乏经验,并且在我的基本翻译后发现我的 C++ 函数 运行 慢了很多,这让我感到震惊。任何关于为什么会这样,或者我可以做些什么来改进我的 C++ 代码的任何指示都将不胜感激。
脚本概述:该函数从另一个程序读取标准输入,检查每一行是否有任何子字符串匹配,并将每一行打印到标准输出
Python function:
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:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_
c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65RG: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 秒
C++ = 1.245 秒
但是我的文件只有 156,980 行,所以这不是全部问题。
拆分将片段复制到新的字符串中。这很慢,您不需要它们。而是在行中搜索您想要的片段的开头(第 10 个),然后从那里开始调用查找。
我意识到我的原始代码不适合测试,所以我想我会在这里重构它,并讨论我的发现。 我按照建议使用 -Ofast(最快,积极的优化,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 毫秒!所以看来 boost::split 是一个问题。感谢您的建议。
尝试对这段代码进行一些优化
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("%s\n", line.c_str());
break; // Stop looking
}
}
}
}
}