Vergleich C++ vs. Python

From revampedia
Jump to: navigation, search

Motivation

Als ich den apybot programmiert habe hatte ich Lust die Performance von Python mit einer anderen Programmiersprache vergleichen.

Auch da ich finde, dass sich die Parserfunktion für IRC Nachrichten dafür gut eignet. Diese ist ziemlich einfach zu programmieren und lässt sich in einer Schleife wiederholen. Ob das wirklich einen guten Benchmark ergibt, kann ich nicht wirklich beurteilen. (Ist mir aber auch egal.)

Code

Der Code soll eine Datei mit IRC Nachrichten lesen. Diese in prefix, command, arguments und text, teilen und zurückliefern.

Python Code:

#!/usr/bin/python3
#
# test parser speed
#
# myparser
#

def split_recv_msg(line):
    '''Split received IRC message into defined parts.

Returns: prefix command args text'''

    # opt: use else instead of default value to avoid write
    #prefix = None
    #text = None

    if line[0] == ':':
        prefix, line = line[1:].split(' ', 1)
    else:
        prefix = None

    if line.find(' :') != -1:
        line, text = line.split(' :', 1)
    else:
        text = None

    if line.find(' ') != -1:
        command, line = line.split(' ', 1)
        args = line.split()
    else:
        command = line
        args = None

    return prefix, command, args, text

TESTFILE = "output.file.big"

for line in open(TESTFILE, 'r'):

    sline = split_recv_msg(line)
    #sline = split_recv_msg(line)

    #print("sline: ", sline)

C++ Code:

// parsing IRC message in c++

#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <fstream>
#include <sstream>
//using namespace std;


struct msg_struct {
    std::string prefix;
    std::string command;
    std::vector<std::string> args;
    std::string text;
};

void split_msg_line(std::string& line, msg_struct& line_parts) {

    // (string and vector are empty by default!
    // still have to set it in the else or it will stay
    // at the value of the last run...)

    // get prefix
    if (line[0] == ':') {
        std::size_t pref_break_pos = line.find(' ');

        // set prefix
        line_parts.prefix = line.substr(1, pref_break_pos-1);

        line = line.substr(pref_break_pos+1);
        //std::cout << line << std::endl;
    }
    else {
        line_parts.prefix.clear();
    }

    // get text
    std::size_t text_break_pos = line.find(" :");
    if (text_break_pos != std::string::npos) {
        // set text
        line_parts.text = line.substr(text_break_pos+2);

        line = line.substr(0, text_break_pos);
        //std::cout << '"' << line << '"' << std::endl;
    }
    else {
        line_parts.text.clear();
    }

    // get command
    // (clear args vector!)
    line_parts.args.clear();
    std::size_t command_end_pos = line.find(' ');
    if (command_end_pos != std::string::npos) {
        line_parts.command = line.substr(0, command_end_pos);

        line = line.substr(command_end_pos+1);
        //std::cout << '"' << line << '"' << std::endl;

        // parse args
        // http://stackoverflow.com/questions/236129/split-a-string-in-c
        int start = 0, end = 0;
        while ((end = line.find(' ', start)) != std::string::npos) {
            line_parts.args.push_back(line.substr(start, end - start));
            start = end + 1;
        }
        line_parts.args.push_back(line.substr(start));
    }
    // else command=line
    else {
        line_parts.command = line;
    }


}

int main() {

    // initialize output structure
    msg_struct line_parts_out;

    // line examples
    //std::string regi_str = ":orwell.freenode.net 001 pybot_ :Welcome to the freenode Internet Relay Chat Network pybot_";
    //std::string regi_str = "001 :hoho";
    //std::string regi_str = "001 gaga abc :Welcome to the freenode Internet Relay Chat Network pybot_";

    // read file line by line
    // http://stackoverflow.com/questions/7868936/read-file-line-by-line
    std::ifstream infile("output.file.big");

    // process line by line
    std::string line;
    while (std::getline(infile, line)) {

        split_msg_line(line, line_parts_out);

        // concise output
/*        std::cout << "p: " << '"' << line_parts_out.prefix << '"';
        std::cout << " c: " << '"' << line_parts_out.command << '"';

        std::cout << " a: [ ";

        std::vector<std::string>::iterator it;
        for (it=line_parts_out.args.begin(); it<line_parts_out.args.end(); it++)
            std::cout << '"' << *it << '"' << ' ' ;

        std::cout << "]";

        std::cout << " t: " << '"' << line_parts_out.text << '"' << std::endl;
*/
        // verbose output
/*        std::cout << "prefix: " << '"' << line_parts_out.prefix << '"' << std::endl;
        std::cout << "text: " << '"' << line_parts_out.text << '"' << std::endl;
        std::cout << "command: " << '"' << line_parts_out.command << '"' << std::endl;

        std::cout << "args: [ ";

        std::vector<std::string>::iterator it;
        for (it=line_parts_out.args.begin(); it<line_parts_out.args.end(); it++)
            std::cout << '"' << *it << '"' << ' ' ;

        std::cout << "]" << std::endl;
*/
    }

}

Durchführung

Getested habe ich mit einer Datei die 34220 IRC Zeilen enthält (erzeugt in bash durch wiederholung von ca. 10 Zeilen, mit `cat` und `for`).

Um die Laufzeit nicht zu beeinträchtigen wird auf eine Ausgabe verzichtet.

Die Zeit habe ich mittels `time` befehl gestoppt.

Beispiel:

$ time ./parse-my.py

real	0m0.210s
user	0m0.196s
sys	0m0.016s

Resultate

Der C++ Code ist auch um einiges länger, komplizierter und war für mich wesentlich schwieriger zu schreiben. Die Entwicklung hat auch entsprechen ca. 10x solange gedauert.

Python braucht ca 0.2 Sekunden. C++ erledigt das in ca. 0.1 Sekunden.