为什么 awk 脚本比 C++ 程序更快?

Jos*_* M. -1 c++

背景:我是 C++ 领域的 rockie

输入文件: 100万行

FCC5G2YACXX:5:1101:1224:2059#NNNNNNNN 97 基因组 96003934 24 118M4D11M = 96004135 0 GCA....ACG P\..GW^EO AS:i:-28 XN:i:0 XM:i:2 XO: i:1 XG:i:4 NM:i:6 MD:Z:54G53T9^TACA11 YT:Z:UP

预期产出

96003934 98.31

解释输出

第 4 栏:96003934

第 18 栏:MD:Z:54G53T9^TACA11

匹配 = 54+53+9 = 116

不匹配 = count_letter(54G53T9) = 2

id = 116*100 / (116+2) = 98.30508474576272

awk 脚本

awk '{
    split($18,v,/[\^:]/); 
    nmatch = split(v[3],vmatch, /[^0-9]/); 
    cmatch=0; 
    for(i=1; i<=nmatch; i++) cmatch+=vmatch[i]; 
    printf("%s"OFS"%.2f\n", $4, cmatch*100/(cmatch+nmatch-1));
}' file.sam
Run Code Online (Sandbox Code Playgroud)

C++,我认为会更快

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <iomanip>

using namespace std;

int main(){
  string line;
  while(getline(cin, line)){
    istringstream iss(line);
    vector<string> columns;
    copy(istream_iterator<string>(iss),    //Split line by spaces
         istream_iterator<string>(),
         back_inserter(columns));
    //I extract information from column 18
    int start = columns[17].find_last_of(':');
    int end = columns[17].find_first_of('^');
    string smatch = columns[17].substr(start+1, end-start-1);
    // I get for example "54G53T9"
    replace( smatch.begin(), smatch.end(), 'A', ' ');
    replace( smatch.begin(), smatch.end(), 'C', ' ');
    replace( smatch.begin(), smatch.end(), 'G', ' ');
    replace( smatch.begin(), smatch.end(), 'T', ' ');
    // I get for example "54 53 9"
    istringstream iss_sum(smatch);
    int n=0, sum=0, count=0;
    while(iss_sum >> n){
      sum += n;
      count++;
    }
    cout << columns[3] << ' ' << fixed << setprecision(2) 
         << (float)sum*100 / (sum+count-1) << endl;
  }
}
Run Code Online (Sandbox Code Playgroud)

基准

输入有 100 万行......

  • awk: 0m6.102s
  • C++:0m15.814s

问题

我做错了什么,所以C++工作缓慢?.....我可以改进C++程序吗?如果是,怎么办?.....我应该写进去C吗?....

预先感谢

Jer*_*fin 5

C++ iostreams 并没有真正提供一种好的方法来检查某些输入中是否存在列,但会忽略它。C++ iostreams 有一个ignore,但它不太适合这种特殊情况,所以它可能没有帮助。

既然如此,我至少会考虑使用scanf,可能是这个一般顺序上的东西:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <string>
#include <iostream>
#include <numeric>

int main() {
    char column4[256];
    char column17[256];

    while (2 == scanf("%*s %*s %*s %255s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %255s %*s", column4, column17)) {
        char *beg = strrchr(column17, ':') + 1;
        char *end = strchr(column17, '^');

        *end = '\0';

        int nums[5];

        int count = sscanf(beg, "%d%*[A-Z]%d%*[A-Z]%d%*[A-Z]%d%*[A-Z]%d", nums, nums + 1, nums + 2, nums + 3, nums + 4);


        int sum = std::accumulate(nums, nums + count, 0);

        double result = (sum*100.0) / (sum + count-1);
        printf("%s %2.2f\n", column4, result);
    }
}
Run Code Online (Sandbox Code Playgroud)

目前,这假设(也许是错误的,但我必须猜测一些)第 17 列(或者,我将其算作第 18 列,但无论如何)从开始到最后一个冒号 ( ) 都可以被忽略:。然后是任意数量的数字重复,然后是字母,另一个数字,另一个字母等等(暂时假设以数字开头和结尾)。目前,我最多允许使用 5 个数字,但允许更多数字就没什么意义了。允许模式中有更多变化可能需要更多的工作(取决于可能发生哪种变化。

为了提高速度,您可以使用更大的输入缓冲区,如下所示:

setvbuf(stdin, NULL, _IOFBF, 65536);
Run Code Online (Sandbox Code Playgroud)

您需要/想要在阅读任何内容之前执行此操作,因此它会在while循环之前执行。确切地说,这会带来多大的好处(如果有的话)似乎有所不同,但它很容易做到,至少值得尝试一下,看看它是否有任何不同。