Bash is een interpreted language, en daarmee automatisch niet snel.
Je hebt eigenlijk 3 soorten programmeertalen:
- Interpreted. Draait binnen een interpreter, zoals bash. Iedere regel wordt door de interpreter bekeken en uitgevoerd
- Compiled. Maakt echte executables. Source moet gecompiled worden en resultaat is een echte .exe of ELF, die direct kunnen worden uitgevoerd.
- Intermediate_representation (
Wikipedia: Intermediate language). Script wordt voor het uitvoeren door een compiler-achtige interpreter heen gehaald en wordt uiteindelijk als zgn byte-code uitgevoerd.
Omdat ik ook nog e.e.a. wou proberen met verschillende IDEs, en op zoek was naar een simpel programmaatje om te maken, heb ik het maar eens geprobeerd.
Ik heb eerst een random "csv1" gegenereerd:
code:
1
2
3
| for i in {1..10000}; do
for> echo $(uuidgen),$(shuf -n 1 /usr/share/dict/cracklib-small) >> /tmp/csv1.csv
for> done |
(dit duurde overigens zo'n 5 minuten)
Hierna een "csv2":
code:
1
2
3
4
| cat /tmp/csv1.csv | while read line; do
pipe while> bar=$(echo ${line} | awk -F , '{print $1}')
pipe while> echo ${bar},$(uuidgen),$(uuidgen),1,1 >> /tmp/csv2.csv
pipe while> done |
Beide files staan op tmpfs, om te voorkomen dat we hier I/O aan het benchmarken zijn. Ik maak expres geen gebruik van modules, templates o.i.d., anders zijn we hier CSV frameworks aan het benchmarken.
bash:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #!/bin/bash
starttime=$(date +%s%N)
echo "Started at ${starttime}"
cat /tmp/csv1.csv | while read line; do
bar=$(echo $line | awk -F , '{print $1}')
fullinfo=$(grep "${bar}" /tmp/csv2.csv)
echo ${line},$(echo "${fullinfo}" | cut --complement -d ',' -f 1) >> /tmp/csv3.csv
done
endtime=$(date +%s%N)
echo "Done at ${endtime}"
echo "Time taken $(expr ${endtime} - ${starttime}) nanoseconds" |
Resultaat:
./tweak.sh
Started at 1451337274579537852
Done at 1451337427989920823
Time taken 153410382971 nanoseconds
153 seconden, of ~ 2.5 minuten
Python
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| from time import time
starttime = time()
print("Started at {}".format(starttime))
f1 = open('/tmp/csv1.csv')
f2 = open('/tmp/csv2.csv')
f3 = open('/tmp/csv3python.csv', mode='w')
for line in f1:
barcode = line.split(',')[0]
for fullline in f2:
if fullline.startswith(barcode):
extrainfo = fullline[fullline.index(',') + 1:]
newline = '{},{}'.format(line.strip('\n'), extrainfo)
f2.seek(0)
f3.write('{}'.format(newline))
endtime = time()
print('Done at {}'.format(endtime))
print('Taken {} seconds'.format(endtime-starttime)) |
Resultaat:
Started at 1451338532.7614973
Done at 1451338651.3239393
Taken 118.56244206428528 seconds
118 seconden, net geen 2 minuten.
C++
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| #include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <chrono>
int main() {
auto starttime = std::chrono::high_resolution_clock::now();
std::ifstream f1("/tmp/csv1.csv");
std::ifstream f2("/tmp/csv2.csv");
std::ofstream f3("/tmp/csv3cplusplus.csv");
std::string f1_line;
std::string f2_line;
std::string newline;
while(std::getline(f1, f1_line)) {
std::string barcode = f1_line.substr(0, f1_line.find(','));
//Find barcode in f2
f2.clear();
f2.seekg(0);
while(std::getline(f2, f2_line)) {
// Check if barcode is at start of line
if(f2_line.find(barcode) == 0) {
newline = f1_line + "," + f2_line.substr(f2_line.find(',') + 1);
}
}
f3 << newline;
}
f3.close();
auto endtime = std::chrono::high_resolution_clock::now();
std::cout << "Program finished in " <<
std::chrono::duration_cast <std::chrono::nanoseconds> (endtime-starttime).count() <<
" nanoseconds\n";
return 0;
} |
Resultaat:
Program finished in 18621363489 nanoseconds
18.6 seconden. Een factor 7 sneller. Dit kan natuurlijk fluctueren afhankelijk van de workload.
Ondanks dat het dus wel het meest ingewikkeld is, zeker als je met grote datasets moet gaan werken, kan het het zeker waard zijn om een "echt" programma te schrijven.
We are pentium of borg. Division is futile. You will be approximated.