Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does locking slow down this sequential file parser?

I wrote a simple reader and parser for a graph file format. The problem is that it is incredibly slow. Here are the relevant methods:

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;
}

To diagnose why it is so slow, I ran it in a profiler (Apple Instruments). The results were surprising: It's slow because of locking overhead. The program spends over 90% of its time in pthread_mutex_lock and _pthread_cond_wait.

Instruments

I have no idea where the locking overhead comes from, but I need to get rid of it. Can you suggest next steps?

EDIT: See the call stack expanded for _pthread_con_wait. I cannot figure out the source of the locking overhead by looking at this:

enter image description here

like image 203
clstaudt Avatar asked Jan 23 '13 15:01

clstaudt


2 Answers

Expand the call stack on the _pthread_cond_wait and pthread_mutex_lock calls to find out where the locking calls are invoked from.

As a guess I'm going to say it's in all the unnecessary heap allocations you're doing. The heap is a thread safe resource and on this platform the thread safety could be provided via mutexes.

like image 59
karunski Avatar answered Nov 10 '22 15:11

karunski


All functions that read data from an istream will lock a mutex, read data from a streambuf and unlock the mutex. To eliminate that overhead, read the file directly from the streambuf instead of the istream and don't use stringstream to parse the data.

Here is a version of getline that uses streambuf instead of 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;
    }
}
like image 42
Johan Råde Avatar answered Nov 10 '22 13:11

Johan Råde