Vergleich C++ vs. Python
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.