По-разному ли пишут на диск C++ и Python?

Итак, несколько лет назад я написал программу на Python, которая записывает английские имена последовательного списка чисел в файл (один, два, три и т. д.). Я работал над тем, чтобы версия C++ работала в течение последнего месяца (личный проект), и я думаю, что она у меня работает довольно хорошо. Одна проблема: это в пять раз медленнее, чем версия Python. Я пробовал переключать методы конкатенации строк (<< vs operator+ vs operator+= vs .append()), используя fprinf() вместо ofstream, предварительно выделяя размер строки (.reserve()) и многое другое, что я не могу вспомнить, но, похоже, врезались в стену. Затем я заметил, что скорость записи C++, кажется, составляет около 70 МБ / с, тогда как версия Python записывает около 350 МБ / с. Я использую диск со скоростью вращения 5400 об / мин (CrystalDiskMark дает скорость последовательной записи 60-90 МБ / с), так что скорости записи C++ правдоподобны, но Python?

TL; DR: Кажется, Python пишет в пять раз быстрее, чем возможно (почти со скоростью чтения!) С диска.

Я включил программы ниже, на случай, если мне не хватает чего-то очевидного (правдоподобного). «Бенчмаркинг» включал запуск каждой программы для чисел 1–1 000 000, в результате чего получился файл размером 50 824 КБ. ~ 50 с для C++, ~ 8,5 с для Python.

Python:

##Code in Python version 2.7.5 for generating a file with the English names of a set range of numbers.
while 1:
    s=input("Starting_Value:")
    f=input("Ending_Value:")
    filename=raw_input("Desired Name of File:")   

    ##dictionary
    one=["","one","two","three","four","five","six","seven","eight","nine","error_one"]
    one2=["","-one","-two","-three","-four","-five","-six","-seven","-eight","-nine","error_one2"]
    teen=["ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen","error_teen"]
    ten=["","twenty","thirty","fourty","fifty","sixty","seventy","eighty","ninety","error_ten"]

    ##Functions
    def translate(n): ##handles '' to 999
        work=[]
        if n>=100:
            work.append(one[int(n/100)]+" hundred ")
            n=n-(100*int(n/100))
            if n>=10:
                if n>=20:
                    work.append(ten[int(n/10)-1]+one2[n-(10*int(n/10))]+" ")
                else:
                    work.append(teen[n%10]+" ")
            elif n>=1:
                work.append(one[n]+ " ")
        elif n>=10:
            if n>=20:
                work.append(ten[int(n/10)-1]+one2[n-(10*int(n/10))]+" ")
            else:
                work.append(teen[n%10]+" ")
        elif n>=1:
            work.append(str(one[n])+" ")
        end1=', '.join(work)
        end1=end1.replace(", ","")
        return end1

    def english(m): ##handles billions, hundred millions, millions, hundred thousands, thousands
        work2=[]
        if m>=1000000000:
            work2.append(str(translate(int(m/1000000000)))+"billion ")
        elif m>=1000000:
            work2.append(str(translate(int(m/1000000)))+"million "+str(translate(int(m-(1000000*int(m/1000000)))/1000))+"thousand "+str(translate(m-(1000*int(m/1000)))))
            if ((int(m/1000)%1000)==0):
                end3=str(', '.join(work2))
                end4=end3.replace("thousand ", "")
                work2[:]=[]
                work2=[str(end4)]
            else:
                end3=str()
        elif m>=1000:
            work2.append(str(translate(int(m/1000)))+"thousand "+str(translate(m%1000)))
        elif m>=1:
            work2.append(translate(m))
        end2=str(', '.join(work2))
        end2=end2[:-1]
        return end2
    ##Main Program - Iterator
    file = open(str(filename), "a")
    for i in range(f-s+1):
        file.write(str(english(s))+", ")
        s = s + 1
    file.close()
    a = raw_input("Close window to EXIT, or press ENTER to generate another file")

C++

//Generates a file of sequential numbers in English

//libraries
#include <iostream> //for output to terminal
#include <fstream>  //for output to file
#include <string>   //for handling strings

using namespace std;        //yes

ofstream fout;              //for convenience with 'cout'

//function prototypes
string thousands(int n);    //translates 1 to 999
string millions(int m);     //translates the hundred thousands, millions, 
hundred millions
int lint(int j, int k);     //outputs the digits of a number greater than the kth place i.e. lint(123456, 1000) = 123

//variables
int shi = 0;                //starting value
int kut = 1;                //ending value
int i = 0;                  //iterator
string fname = "";          //filename
string buffern = "";        //buffer for thousands
string bufferm = "";        //buffer for millions
string bufferf = "";        //first digit buffer

//dictionary
char one[10][7] = { ""," one"," two"," three"," four"," five"," six"," seven"," eight"," nine" };
char one2[10][7] = { "","-one","-two","-three","-four","-five","-six","-seven","-eight","-nine" };
char teen[10][11] = { " ten"," eleven"," twelve"," thirteen"," fourteen"," fifteen"," sixteen"," seventeen"," eighteen"," nineteen" };
char ten[10][9] = { "",""," twenty"," thirty"," fourty"," fifty"," sixty"," seventy"," eighty"," ninety" };

//main function
int main()
{
    while (1)
    {
        //get user input
        cout << " Starting Number: ";
        cin >> shi;
        cout << " Ending Number: ";
        cin >> kut;
        while (fout.is_open() != 1)
        {
            cout << " Desired Filename: ";
            cin >> fname;
            fname.append(".txt");
            fout.open(fname);
            if (fout.is_open() != 1)
                cout << "\n   Invalid file name.  Please try again.\n";
        }

        //translate and write to file 
        if (shi == 0) {                 //handles starting at zero
            fout << "zero,";
            shi = 1;
        }
        else                            //handles spacing for next word
        {
            bufferf = millions(shi);
            bufferf.erase(0, 1);
            bufferf += ",";
            fout << bufferf;
            shi++;
        }
        for (i = shi; i < (kut); ++i)   //Main Iterator
        {
            fout << millions(i) << ",";
        }
        fout << millions(kut) << ".";   //handles last word
        fout.close();

        //display confirmation and prompt to exit/continue
        cout << "\n Complete\n";
        cin.get();
        cin.ignore();
        cout << endl;
    }
}

//function definitions
string thousands(int n)     //writes '' to 999
{
    buffern = ""; 
    buffern.reserve(30);
    if (n >= 100) {                     //write hundreds place
        buffern += one[lint(n, 100)];
        buffern += " hundred";
        n = n % 100;
        if (n >= 10) {                  //write tens place
            if (n >= 20) {
                buffern += ten[lint(n, 10)];
                buffern += one2[n % 10];
            }
            else {                      //deal with 'teens'
                buffern += teen[n % 10];
            }
        }
        else if (n >= 1) {              //write ones place
            buffern += one[n % 10];
        }
    }
    else if (n >= 10) {                 //write tens place
        if (n >= 20) {
            buffern += ten[lint(n, 10)];
            buffern += one2[n % 10];
        }
        else {                          //deal with 'teens'
            buffern += teen[n % 10];
        }
    }
    else if (n >= 1) {                  //write ones place
        buffern += one[n];
    }
    return buffern;
}

string millions(int m)
{
    bufferm = "";
    bufferm.reserve(100);
    if (m >= 1000000)
    {
        if (int(m / 1000) % 1000 == 0) {                //if xxx,000,xxx
            bufferm += thousands(lint(m, 1000000));
            bufferm += " million";
            bufferm += thousands(m % 1000);
        }
        else {
            bufferm += thousands(lint(m, 1000000));     //millions
            bufferm += " million";
            bufferm += thousands(lint(m, 1000) % 1000); //hundred thousands
            bufferm += " thousand";
            bufferm += thousands(m % 1000);             //thousands
        }
    }
    else if (m >= 1000) {
        bufferm += thousands(lint(m, 1000));            //hundred thousands
        bufferm += " thousand";
        bufferm += thousands(m % 1000);                 //thousands
    }
    else if (m >= 1) {
        bufferm += thousands(m);                        //thousands
    }
    return bufferm;
}

int lint(int j, int k)
{
    return ((j - (j % k)) / k);
}

Я был бы признателен за любое понимание того, почему программы работают с разной скоростью, как Python пишет так быстро, или предложения по ускорению кода C++.

Редактировать: @VTT был прав, там был не весь код C++. Добавлен.

Оба будут использовать один и тот же API ввода-вывода, предоставляемый операционной системой. Так что разница в производительности может возникнуть только из-за этого причудливого словесного кода. Также обычный вопрос: включали ли вы оптимизацию при компиляции кода на C++? А реализация на C++ кажется неполной, например нет функции millions.

user7860670 02.05.2018 00:27

Несвязанный. В этом коде C++ много глобальных переменных. Держу пари, они тебе не нужны.

user4581301 02.05.2018 00:38

Более полезно то, что ваша среда разработки может поставляться с программным обеспечением для профилирования. Запустите его и посмотрите, что съедает все ваше время.

user4581301 02.05.2018 00:44

@VTT Хорошо, оптимизация не была включена ... C++ теперь работает практически с той же скоростью, что и Python. Спасибо! Все еще не уверен, почему он может писать так быстро ... Может ли это быть из-за того, что серийная скорость намного выше, чем измеряет CrystalDisk?

IronEagle 02.05.2018 00:45

@IronEagle: Операционные системы (и даже библиотеки) выполняют кэширование / буферизацию. Поскольку вы не сбрасываете / не синхронизируете изменения на диске, вы на самом деле не ждете / не измеряете время, необходимое для фактической записи данных на диск - вот почему вы видите скорость выше, чем ваш диск.

Acorn 02.05.2018 00:45

@Acorn: не считая того, что передачи могут происходить по DMA.

Yves Daoust 02.05.2018 11:12
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
6
185
0

Другие вопросы по теме