Vraag Waarom is het lezen van regels van stdin veel langzamer in C ++ dan in Python?


Ik wilde de leesregels van stringinput van stdin vergelijken met Python en C ++ en was geschokt toen ik zag dat mijn C ++-code een orde van grootte langzamer verliep dan de equivalente Python-code. Omdat mijn C ++ roestig is en ik nog geen expert Pythonista ben, vertel me alsjeblieft of ik iets verkeerd doe of dat ik iets verkeerd interpreteer.


(Antwoord TLDR: neem de verklaring op: cin.sync_with_stdio(false) of gewoon gebruiken fgets in plaats daarvan.

TLDR-resultaten: scroll helemaal naar beneden naar de onderkant van mijn vraag en kijk naar de tabel.)


C ++ code:

#include <iostream>
#include <time.h>

using namespace std;

int main() {
    string input_line;
    long line_count = 0;
    time_t start = time(NULL);
    int sec;
    int lps;

    while (cin) {
        getline(cin, input_line);
        if (!cin.eof())
            line_count++;
    };

    sec = (int) time(NULL) - start;
    cerr << "Read " << line_count << " lines in " << sec << " seconds.";
    if (sec > 0) {
        lps = line_count / sec;
        cerr << " LPS: " << lps << endl;
    } else
        cerr << endl;
    return 0;
}

// Compiled with:
// g++ -O3 -o readline_test_cpp foo.cpp

Python-equivalent:

#!/usr/bin/env python
import time
import sys

count = 0
start = time.time()

for line in  sys.stdin:
    count += 1

delta_sec = int(time.time() - start_time)
if delta_sec >= 0:
    lines_per_sec = int(round(count/delta_sec))
    print("Read {0} lines in {1} seconds. LPS: {2}".format(count, delta_sec,
       lines_per_sec))

Dit zijn mijn resultaten:

$ cat test_lines | ./readline_test_cpp
Read 5570000 lines in 9 seconds. LPS: 618889

$cat test_lines | ./readline_test.py
Read 5570000 lines in 1 seconds. LPS: 5570000

Bewerk:  Ik moet vermelden dat ik dit zowel onder Mac OS X v10.6.8 (Snow Leopard) als Linux 2.6.32 (Red Hat Linux 6.2) heb geprobeerd. De eerste is een MacBook Pro, en de laatste is een zeer krachtige server, niet dat dit te relevant is.

Bewerk 2:  (Deze bewerking is verwijderd, omdat deze niet langer van toepassing is)

$ for i in {1..5}; do echo "Test run $i at `date`"; echo -n "CPP:"; cat test_lines | ./readline_test_cpp ; echo -n "Python:"; cat test_lines | ./readline_test.py ; done
Test run 1 at Mon Feb 20 21:29:28 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 2 at Mon Feb 20 21:29:39 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 3 at Mon Feb 20 21:29:50 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 4 at Mon Feb 20 21:30:01 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 5 at Mon Feb 20 21:30:11 EST 2012
CPP:   Read 5570001 lines in 10 seconds. LPS: 557000
Python:Read 5570000 lines in  1 seconds. LPS: 5570000

Bewerk 3:

Oké, ik probeerde de suggestie van J.N. om te proberen de Python-winkel de regel te laten lezen: maar het maakte geen verschil uit voor de snelheid van Python.

Ik heb ook de suggestie van J.N. geprobeerd te gebruiken scanf in een char array in plaats van getline in een std::string. Bingo! Dit resulteerde in dezelfde prestaties voor zowel Python als C ++. (3,333,333 LPS met mijn invoergegevens, die trouwens slechts korte regels zijn van elk drie velden, meestal ongeveer 20 tekens breed, hoewel soms meer).

Code:

char input_a[512];
char input_b[32];
char input_c[512];
while(scanf("%s %s %s\n", input_a, input_b, input_c) != EOF) {
    line_count++;
};

Snelheid:

$ cat test_lines | ./readline_test_cpp2
Read 10000000 lines in 3 seconds. LPS: 3333333
$ cat test_lines | ./readline_test2.py
Read 10000000 lines in 3 seconds. LPS: 3333333

(Ja, ik heb het meerdere keren uitgevoerd.) Dus ik denk dat ik het nu ga gebruiken scanf in plaats van getline. Maar ik ben nog steeds nieuwsgierig of mensen denken dat deze uitvoering is getroffen std::string/getline is typisch en redelijk.

Bewerk 4 (was: Final Edit / Solution):

Het toevoegen van:

cin.sync_with_stdio(false);

Direct boven mijn oorspronkelijke while-loop hierboven resulteert code die sneller werkt dan Python.

Nieuwe prestatievergelijking (dit is op mijn 2011 MacBook Pro), met behulp van de originele code, het origineel met de sync uitgeschakeld en de originele Python-code, respectievelijk, op een bestand met 20M tekstregels. Ja, ik heb het een aantal keren gebruikt om het wissen van schijfcaches te elimineren.

$ /usr/bin/time cat test_lines_double | ./readline_test_cpp
       33.30 real         0.04 user         0.74 sys
Read 20000001 lines in 33 seconds. LPS: 606060
$ /usr/bin/time cat test_lines_double | ./readline_test_cpp1b
        3.79 real         0.01 user         0.50 sys
Read 20000000 lines in 4 seconds. LPS: 5000000
$ /usr/bin/time cat test_lines_double | ./readline_test.py
        6.88 real         0.01 user         0.38 sys
Read 20000000 lines in 6 seconds. LPS: 3333333

Met dank aan @Vaughn Cato voor zijn antwoord! Elke uitwerking die mensen kunnen maken of goede referenties waarop mensen kunnen wijzen waarom deze synchronisatie gebeurt, wat het betekent, wanneer het nuttig is en wanneer het OK is om uit te schakelen, zou zeer gewaardeerd worden door het nageslacht. :-)

Bewerk 5 / betere oplossing:

Zoals voorgesteld door Gandalf The Grey hieronder, gets is zelfs sneller dan scanf of de niet-gesynchroniseerd cin nadering. Ik heb dat ook geleerd scanf en gets zijn beide UNSAFE en mogen NIET WORDEN GEBRUIKT vanwege het potentieel van bufferoverloop. Dus ik heb deze iteratie geschreven met behulp van fgets, het veiligere alternatief voor krijgt. Hier zijn de relevante regels voor mijn mede-noobs:

char input_line[MAX_LINE];
char *result;

//<snip>

while((result = fgets(input_line, MAX_LINE, stdin )) != NULL)
    line_count++;
if (ferror(stdin))
    perror("Error reading stdin.");

Nu, hier zijn de resultaten met behulp van een nog groter bestand (100M lijnen, ~ 3,4 GB) op een snelle server met een zeer snelle schijf, een vergelijking van de Python-code, de niet-gesynchroniseerde cin, en de fgetsbenaderingen, evenals vergelijken met de wc utility. [De scanf versie segmentatie fout en ik heb geen zin om het probleem op te lossen.]:

$ /usr/bin/time cat temp_big_file | readline_test.py
0.03user 2.04system 0:28.06elapsed 7%CPU (0avgtext+0avgdata 2464maxresident)k
0inputs+0outputs (0major+182minor)pagefaults 0swaps
Read 100000000 lines in 28 seconds. LPS: 3571428

$ /usr/bin/time cat temp_big_file | readline_test_unsync_cin
0.03user 1.64system 0:08.10elapsed 20%CPU (0avgtext+0avgdata 2464maxresident)k
0inputs+0outputs (0major+182minor)pagefaults 0swaps
Read 100000000 lines in 8 seconds. LPS: 12500000

$ /usr/bin/time cat temp_big_file | readline_test_fgets
0.00user 0.93system 0:07.01elapsed 13%CPU (0avgtext+0avgdata 2448maxresident)k
0inputs+0outputs (0major+181minor)pagefaults 0swaps
Read 100000000 lines in 7 seconds. LPS: 14285714

$ /usr/bin/time cat temp_big_file | wc -l
0.01user 1.34system 0:01.83elapsed 74%CPU (0avgtext+0avgdata 2464maxresident)k
0inputs+0outputs (0major+182minor)pagefaults 0swaps
100000000


Recap (lines per second):
python:         3,571,428
cin (no sync): 12,500,000
fgets:         14,285,714
wc:            54,644,808

Zoals je kan zien, fgets is beter, maar nog steeds vrij ver van wc-prestaties; Ik ben er vrij zeker van dat dit komt door het feit dat WC elk personage onderzoekt zonder geheugenkopieën. Ik vermoed dat op dit punt andere delen van de code de bottleneck worden, dus ik denk niet dat het optimaliseren van dat niveau zelfs de moeite waard zou zijn, zelfs als het mogelijk is (want ik moet de leesregels immers opslaan) in het geheugen).

Merk ook op dat een kleine afweging met het gebruik van een char * buffer en fgets versus niet gesynchroniseerd cin stringen is dat de laatste lijnen van elke lengte kan lezen, terwijl de eerstgenoemde de invoer tot een eindig getal beperkt. In de praktijk is dit waarschijnlijk een non-issue voor het lezen van de meeste regelgebaseerde invoerbestanden, omdat de buffer kan worden ingesteld op een zeer grote waarde die niet zou worden overschreden door geldige invoer.

Dit is educatief geweest. Bedankt aan iedereen voor uw opmerkingen en suggesties.

Bewerk 6:

Zoals gesuggereerd door J.F. Sebastian in de onderstaande opmerkingen, gebruikt het hulpprogramma GNU wc plain C read() (in de wrapper safe-read.c) om chunks (van 16k bytes) tegelijk te lezen en nieuwe regels te tellen. Hier is een Python-equivalent op basis van de code van J.F. (alleen het relevante fragment dat de Python vervangt for lus:

BUFFER_SIZE = 16384
count = sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, BUFFER_SIZE), ''))

De uitvoering van deze versie is vrij snel (hoewel nog steeds een beetje langzamer dan het onbewerkte C wc-nut, natuurlijk):

$ /usr/bin/time cat temp_big_file | readline_test3.py
0.01user 1.16system 0:04.74elapsed 24%CPU (0avgtext+0avgdata 2448maxresident)k
0inputs+0outputs (0major+181minor)pagefaults 0swaps
Read 100000000 lines in 4.7275 seconds. LPS: 21152829

Nogmaals, het is een beetje gek voor mij om C ++ te vergelijken fgets/cin en de eerste pythoncode aan de ene kant aan wc -l en dit laatste Python-fragment aan de andere kant, aangezien de laatste twee de leesregels niet daadwerkelijk opslaan, maar alleen nieuwe regels tellen. Toch is het interessant om alle verschillende implementaties te bekijken en na te denken over de gevolgen voor de prestaties. Nogmaals bedankt!

Edit 7: Tiny benchmark-addendum en samenvatting

Voor de volledigheid dacht ik dat ik de leessnelheid voor hetzelfde bestand zou bijwerken in dezelfde doos met de originele (gesynchroniseerde) C ++ -code. Nogmaals, dit is voor een 100M-regelbestand op een snelle schijf. Dit is de volledige tabel nu:

Implementation      Lines per second
python (default)           3,571,428
cin (default/naive)          819,672
cin (no sync)             12,500,000
fgets                     14,285,714
wc (not fair comparison)  54,644,808

1456
2018-02-21 03:24


oorsprong


antwoorden:


Standaard, cin is gesynchroniseerd met stdio, waardoor er geen invoerbuffering wordt voorkomen. Als je dit aan de top van je main toevoegt, zou je veel betere prestaties moeten zien:

std::ios_base::sync_with_stdio(false);

Normaal gesproken, als een invoerstroom wordt gebufferd, wordt de stream in plaats van één teken tegelijk gelezen in grotere delen. Dit vermindert het aantal systeemaanroepen, die doorgaans relatief duur zijn. Echter, sinds de FILE* gebaseerde stdio en iostreams hebben vaak afzonderlijke implementaties en daarom afzonderlijke buffers, dit zou tot een probleem kunnen leiden als ze allebei samen zouden worden gebruikt. Bijvoorbeeld:

int myvalue1;
cin >> myvalue1;
int myvalue2;
scanf("%d",&myvalue2);

Als er meer invoer is gelezen door cin dan het werkelijk nodig had, dan zou de tweede integer waarde niet beschikbaar zijn voor de scanf functie, die een eigen onafhankelijke buffer heeft. Dit zou tot onverwachte resultaten leiden.

Om dit te voorkomen, worden streams standaard gesynchroniseerd met stdio. Een gebruikelijke manier om dit te bereiken is om te hebben cin lees elk teken één voor één af zoals nodig met behulp van stdio functies. Helaas introduceert dit veel overhead. Voor kleine hoeveelheden input is dit geen groot probleem, maar als je miljoenen regels leest, is de prestatieafspraak aanzienlijk.

Gelukkig hebben de ontwerpers van de bibliotheek besloten dat je deze functie ook moet kunnen uitschakelen om betere prestaties te krijgen als je wist wat je aan het doen was, dus hebben ze de sync_with_stdio methode.


1279
2018-03-11 18:10



Gewoon uit nieuwsgierigheid heb ik een kijkje genomen naar wat er gebeurt onder de motorkap, en ik heb het gebruikt dtruss / strace bij elke test.

C ++

./a.out < in
Saw 6512403 lines in 8 seconds.  Crunch speed: 814050

syscalls sudo dtruss -c ./a.out < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            6
pread                                           8
mprotect                                       17
mmap                                           22
stat64                                         30
read_nocancel                               25958

Python

./a.py < in
Read 6512402 lines in 1 seconds. LPS: 6512402

syscalls sudo dtruss -c ./a.py < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            5
pread                                           8
mprotect                                       17
mmap                                           21
stat64                                         29

115
2018-02-21 03:33



Ik ben hier een paar jaar achter, maar:

In 'Bewerk 4/5/6' van het originele bericht, gebruikt u de constructie:

$ /usr/bin/time cat big_file | program_to_benchmark

Dit is op verschillende manieren verkeerd:

  1. Je bent eigenlijk bezig met het uitvoeren van `kat`, niet je maatstaf. Het CPU-gebruik 'gebruiker' en 'sys' weergegeven met `tijd` zijn die van` cat`, niet uw gebenchmarkte programma. Erger nog, de 'echte' tijd is ook niet per se accuraat. Afhankelijk van de implementatie van `cat` en van pijplijnen in uw lokale besturingssysteem, is het mogelijk dat` cat` een laatste gigantische buffer schrijft en wordt afgesloten lang voordat het leesproces zijn werk voltooit.

  2. Het gebruik van `kat` is onnodig en in feite contraproductief; je voegt bewegende delen toe. Als je een voldoende oud systeem had (dat wil zeggen met een enkele CPU en - in bepaalde generaties computers - I / O sneller dan CPU) - kon het feit dat `kat` draaide, de resultaten aanzienlijk verkleuren. Je bent ook onderworpen aan welke invoer- en uitvoerbuffering en andere verwerking `cat` ook kan doen. (Dit zou je waarschijnlijk een 'Useless Use Of Cat'-prijs opleveren als ik Randal Schwartz was: https://en.wikipedia.org/wiki/Cat_(Unix)#UUOC_(Useless_Use_Of_Cat))

Een betere constructie zou zijn:

$ /usr/bin/time program_to_benchmark < big_file

In deze verklaring is het de schelp die big_file opent, doorgeeft aan je programma (nou ja, eigenlijk aan `tijd` die vervolgens je programma als een subproces uitvoert) als een reeds open bestandsbeschrijver. 100% van de bestandsuitlezing is strikt de verantwoordelijkheid van het programma dat u probeert te benchmarken. Hierdoor krijg je een goed beeld van zijn prestaties zonder valse complicaties.

Ik noem twee mogelijke, maar eigenlijk verkeerde, 'fixes' die ook kunnen worden overwogen (maar ik 'num' ze anders omdat dit geen dingen waren die fout waren in de oorspronkelijke post):

A. Je zou dit kunnen 'repareren' door alleen je programma te timen:

$ cat big_file | /usr/bin/time program_to_benchmark

B. of door de volledige pijplijn te timen:

$ /usr/bin/time sh -c 'cat big_file | program_to_benchmark'

Deze zijn verkeerd om dezelfde redenen als # 2: ze gebruiken nog steeds `kat` onnodig. Ik noem ze om een ​​paar redenen:

  • ze zijn 'natuurlijker' voor mensen die niet helemaal op hun gemak zijn met de I / O-omleidingsfaciliteiten van de POSIX-shell

  • er kunnen gevallen zijn waarin `kat` is nodig (bijvoorbeeld: het bestand dat moet worden gelezen vereist een bepaald recht om toegang te krijgen, en u wilt dat recht niet verlenen aan het te benchmarken programma: `sudo cat / dev / sda | / usr / bin / time my_compression_test - no-output`)

  • in praktijkop moderne machines is de toegevoegde `kat` in de pijplijn waarschijnlijk niet echt consequent

Maar ik zeg dat laatste met enige aarzeling. Als we het laatste resultaat bekijken in 'Bewerk 5' -

$ /usr/bin/time cat temp_big_file | wc -l
0.01user 1.34system 0:01.83elapsed 74%CPU ...

- deze beweert dat `cat` 74% van de CPU heeft verbruikt tijdens de test; en inderdaad is 1,34 / 1,83 ongeveer 74%. Misschien een run van:

$ /usr/bin/time wc -l < temp_big_file

zou alleen de resterende .49 seconden hebben genomen! Waarschijnlijk niet: `cat` moest hier betalen voor de read () systeemaanroepen (of gelijkwaardig) die het bestand van 'disk' hebben overgebracht (eigenlijk buffer cache), evenals de pipe schrijft om ze te bezorgen op` wc`. De juiste test zou nog steeds die read () -aanroepen moeten doen; alleen de write-to-pipe en read-from-pipe calls zouden zijn opgeslagen, en die zouden best goedkoop moeten zijn.

Toch voorspel ik dat je het verschil tussen `cat file | wc -l` en `wc -l <file` en vind een merkbaar (2-cijferig percentage) verschil. Elk van de langzamere tests hebben in absolute tijd een vergelijkbare straf betaald; welke echter een kleinere fractie van zijn grotere totale tijd zou bedragen.

In feite heb ik wat snelle tests gedaan met een 1,5 gigabyte file of garbage, op een Linux 3.13 (Ubuntu 14.04) -systeem, en deze resultaten behaald (dit zijn eigenlijk 'best of 3' resultaten, natuurlijk na het primen van de cache):

$ time wc -l < /tmp/junk
real 0.280s user 0.156s sys 0.124s (total cpu 0.280s)
$ time cat /tmp/junk | wc -l
real 0.407s user 0.157s sys 0.618s (total cpu 0.775s)
$ time sh -c 'cat /tmp/junk | wc -l'
real 0.411s user 0.118s sys 0.660s (total cpu 0.778s)

Merk op dat de resultaten van de twee pijplijnen beweren dat ze meer CPU-tijd (user + sys) hebben gebruikt dan realtime. Dit komt omdat ik de ingebouwde 'time'-opdracht van de shell (Bash) gebruik, die op de hoogte is van de pijplijn; en ik gebruik een machine met meerdere kernen, waarbij afzonderlijke processen in een pijplijn afzonderlijke kernen kunnen gebruiken, waardoor de CPU-tijd sneller is dan in realtime. Met behulp van / usr / bin / time zie ik een kleinere CPU-tijd dan realtime - wat aangeeft dat het alleen de tijd kan krijgen dat het enkele pijplijnelement er op de opdrachtregel doorheen is gegaan. Ook geeft de uitvoer van de shell milliseconden, terwijl / usr / bin / time slechts honderdsten van een seconde geeft.

Dus op het efficiëntieniveau van `wc -l` maakt de` kat` een groot verschil: 409/283 = 1.453 of 45,3% meer realtime, en 775/280 = 2.768, of maar liefst 177% meer CPU gebruikt! Op mijn willekeurige, daar-was-op-de-tijd testdoos.

Ik moet hieraan toevoegen dat er ten minste één ander significant verschil is tussen deze teststijlen, en ik kan niet zeggen of het een voordeel of een fout is; je moet dit zelf beslissen:

Wanneer je `cat big_file | / usr / bin / time my_program`, je programma ontvangt input van een pipe, precies het tempo verzonden door `cat`, en in chunks niet groter dan geschreven door` cat`.

Wanneer u `/ usr / bin / time my_program <big_file` uitvoert, ontvangt uw programma een open bestandsdescriptor voor het eigenlijke bestand. Jouw programma - of in veel gevallen de I / O-bibliotheken van de taal waarin het is geschreven - kunnen verschillende acties ondernemen wanneer ze worden gepresenteerd met een bestandsdescriptor die verwijst naar een regulier bestand. Het kan mmap (2) gebruiken om het invoerbestand in zijn adresruimte in te delen, in plaats van het gebruik van expliciete lees (2) systeemaanroepen. Deze verschillen kunnen een veel groter effect hebben op uw benchmarkresultaten dan de kleine kosten voor het uitvoeren van het `cat`-binaire bestand.

Het is natuurlijk een interessant benchmarkresultaat als hetzelfde programma significant verschillend presteert tussen de twee cases. Het laat zien dat inderdaad het programma of zijn I / O-bibliotheken zijn iets interessants doen, zoals mmap () gebruiken. Dus in de praktijk is het misschien goed om de benchmarks op twee manieren te gebruiken; misschien het "kat" -resultaat verdisconteren met een kleine factor om de kosten van het runnen van `cat` zelf te" vergeven ".


77
2018-03-13 23:04



Ik heb het oorspronkelijke resultaat op mijn computer gereproduceerd met g ++ op een Mac.

De volgende instructies toevoegen aan de C ++ -versie net voor de while loop brengt het in lijn met de Python versie:

std::ios_base::sync_with_stdio(false);
char buffer[1048576];
std::cin.rdbuf()->pubsetbuf(buffer, sizeof(buffer));

sync_with_stdio verbeterde snelheid tot 2 seconden, en het instellen van een grotere buffer bracht het terug naar 1 seconde.


75
2018-03-11 16:37



getline, stream operators, scanf, kan handig zijn als u zich geen zorgen maakt over het laden van bestanden of als u kleine tekstbestanden laadt. Maar als de uitvoering iets is waar je om geeft, moet je het hele bestand gewoon in het geheugen bufferen (ervan uitgaande dat het past).

Hier is een voorbeeld:

//open file in binary mode
std::fstream file( filename, std::ios::in|::std::ios::binary );
if( !file ) return NULL;

//read the size...
file.seekg(0, std::ios::end);
size_t length = (size_t)file.tellg();
file.seekg(0, std::ios::beg);

//read into memory buffer, then close it.
char *filebuf = new char[length+1];
file.read(filebuf, length);
filebuf[length] = '\0'; //make it null-terminated
file.close();

Als je wilt, kun je een stream rond die buffer wikkelen voor meer gemakkelijke toegang zoals deze:

std::istrstream header(&filebuf[0], length);

Als u het bestand beheert, kunt u overwegen om een ​​plat binair gegevensformaat te gebruiken in plaats van tekst. Het is betrouwbaarder om te lezen en te schrijven, omdat je niet met alle onduidelijkheden van witruimte te maken hebt. Het is ook kleiner en veel sneller te ontleden.


23
2018-02-21 03:32



Trouwens, de reden dat de regel telt voor de C ++ -versie is één groter dan de telling voor de Python-versie is dat de eof-vlag alleen wordt ingesteld als er wordt geprobeerd verder te lezen dan eof. Dus de juiste lus zou zijn:

while (cin) {
    getline(cin, input_line);

    if (!cin.eof())
        line_count++;
};

11
2018-02-21 03:17



De volgende code was sneller voor mij dan de andere code die ik tot nu toe heb gepost: (Visual Studio 2013, 64-bit, 500 MB bestand met lijnlengte uniform in [0, 1000)).

const int buffer_size = 500 * 1024;  // Too large/small buffer is not good.
std::vector<char> buffer(buffer_size);
int size;
while ((size = fread(buffer.data(), sizeof(char), buffer_size, stdin)) > 0) {
    line_count += count_if(buffer.begin(), buffer.begin() + size, [](char ch) { return ch == '\n'; });
}

Het verslaat al mijn Python-pogingen met meer dan een factor 2.


8
2018-02-22 02:33



In uw tweede voorbeeld (met scanf ()) reden waarom dit nog langzamer is, kan zijn omdat scanf ("% s") een tekenreeks parseert en zoekt naar een spatie (spatie, tab, nieuwe regel).

Ook, ja, CPython doet wat caching om harde schijven te vermijden.


7



Een eerste element van een antwoord: <iostream> is langzaam. Verdomd traag. Ik krijg een enorme prestatieverbetering met scanf zoals hieronder, maar het is nog steeds twee keer langzamer dan Python.

#include <iostream>
#include <time.h>
#include <cstdio>

using namespace std;

int main() {
    char buffer[10000];
    long line_count = 0;
    time_t start = time(NULL);
    int sec;
    int lps;

    int read = 1;
    while(read > 0) {
        read = scanf("%s", buffer);
        line_count++;
    };
    sec = (int) time(NULL) - start;
    line_count--;
    cerr << "Saw " << line_count << " lines in " << sec << " seconds." ;
    if (sec > 0) {
        lps = line_count / sec;
        cerr << "  Crunch speed: " << lps << endl;
    } 
    else
        cerr << endl;
    return 0;
}

6



Nou, ik zie dat in je tweede oplossing waar je van bent overgestapt cin naar scanf, wat de eerste suggestie was die ik je zou gaan maken (cin is sloooooooooooow). Nu, als u van overstapt scanf naar fgets, zou je een nieuwe boost in de prestaties zien: fgets is de snelste C ++ -functie voor stringinvoer.

Trouwens, ik wist niet van dat synchronisatiegedeelte, leuk. Maar je moet het toch proberen fgets.


5