2013-01-23 10 views
6

私は、グラフファイル形式のための簡単なリーダーとパーサを書いた。問題はそれが非常に遅いということです。関連する方法は次のとおりです。なぜロックがこのシーケンシャルファイルパーサーを遅くするのですか?

Graph METISGraphReader::read(std::string path) { 
    METISParser parser(path); 
    std::pair<int64_t, int64_t> header = parser.getHeader(); 
    int64_t n = header.first; 
    int64_t m = header.second; 

    Graph G(n); 

    node u = 0; 
    while (parser.hasNext()) { 
     u += 1; 
     std::vector<node> adjacencies = parser.getNext(); 
     for (node v : adjacencies) { 
      if (! G.hasEdge(u, v)) { 
       G.insertEdge(u, v); 
      } 
     } 
    } 
    return G; 
} 

std::vector<node> METISParser::getNext() { 
    std::string line; 
    bool comment = false; 
    do { 
     comment = false; 
     std::getline(this->graphFile, line); 
     // check for comment line starting with '%' 
     if (line[0] == '%') { 
      comment = true; 
      TRACE("comment line found"); 
     } else { 
      return parseLine(line); 
     } 

    } while (comment); 
} 

static std::vector<node> parseLine(std::string line) { 
    std::stringstream stream(line); 
    std::string token; 
    char delim = ' '; 
    std::vector<node> adjacencies; 

    // split string and push adjacent nodes 
    while (std::getline(stream, token, delim)) { 
     node v = atoi(token.c_str()); 
     adjacencies.push_back(v); 
    } 
    return adjacencies; 
} 

なぜそれが遅いのかを診断するには、プロファイラ(Apple Instruments)で実行しました。結果は驚くべきものでした。ロックオーバーヘッドのために遅いです。プログラムは、時間の90%以上をpthread_mutex_lock_pthread_cond_waitに費やしています。

Instruments

私は、ロックのオーバーヘッドが来る見当がつかないが、私はそれを取り除く必要があります。あなたは次のステップを提案できますか?

EDIT:_pthread_con_waitの拡張コールスタックを参照してください。

enter image description here

+0

@KonradRudolph、 'のstd :: ifstream'のstreambufを使用していますgetlineのバージョンです。なぜ私はstdinから読むと思いますか? – clstaudt

+0

私はノブだから。 –

+0

ええと、(なぜ)あなたのコードをOpenMPとリンクしていますか? log4cxxはこの依存関係を持ちますか? –

答えて

2

が_pthread_cond_waitにコールスタックを展開し、pthread_mutex_lockのは、ロックコールがから呼び出された場所を見つけるために呼び出します。私はこれを見ることで、ロックのオーバーヘッドの原因を把握することはできません。

私はそれがあなたがやっているすべての不要なヒープ割り当てにあると言うつもりです。ヒープはスレッドセーフリソースであり、このプラットフォームではスレッドセーフティはmutexを介して提供される可能性があります。

+1

上記の拡張呼び出しスタックを見てください。不要なヒープ割り当てはどこで行いますか? – clstaudt

+0

ロックはOpenMPフレームワークで行われます。これは、ここで示したコードの外にあります。作成する一時的な文字列やベクトルに関して、必要以上に多くの割り当てを行っていますが、OpenMPで行っていることと比べて小さなジャガイモです。 – karunski

+0

"作成する一時的な文字列とベクトルに関して、必要以上に多くの割り当てを行っています。おそらく、これらの割り当てはスタックにありますか?そうではありませんか? – clstaudt

1

istreamからデータを読み取るすべての関数は、mutex,のデータをstreambufから読み取ってロックし、mutexのロックを解除します。そのオーバーヘッドを解消するには、istreamの代わりにstreambufからファイルを直接読み取って、stringstreamを使用してデータを解析しないでください。ここで

代わりに私は、ファイルから読み込んistream

bool fastGetline(streambuf* sb, std::string& t) 
{ 
    t.clear(); 
    for(;;) { 
     int c = sb->sbumpc(); 
     switch (c) { 
     case '\n': 
      return true; 
     case '\r': 
      if(sb->sgetc() == '\n') 
       sb->sbumpc(); 
      return true; 
     case EOF: 
      return !t.empty(); 
     default: 
      t += (char)c; 
    } 
} 
+0

それは理にかなっています。私は明示的に 'fastGetline'を使ってパーサーを実装しようとしますが、それが動作するかどうかがわかります。 – clstaudt

+0

''\ r'' ** not **の後に'' \ n''がある場合、 'fastGetline'はまだ終了します。私はpush_backを '' \ r''にして読み続けるほうがよいと信じています。しかし、それは単体の "\ r"を扱う方法の判断の問題です。とにかく面白い答えです。 – Ali

+0

@Ali Standaloneは、約10年前まではMac上で行末として使用されていました。 – user763305

関連する問題