Объяснение разницы в производительности D и C ++ - PullRequest
3 голосов
/ 30 апреля 2011

Простой пример в D:

import std.stdio, std.conv, core.memory;

class Foo{
    int x;
    this(int _x){x=_x;}
}

void main(string args[]) {
    GC.disable();
    int n = to!int(args[1]);
    Foo[] m= new Foo[n];
    for(int i=0;i<n;i++){
    m[i] = new Foo(i);
    }
}

C ++ код:

#include <cstdlib>
using namespace std;
class Foo{
public:
    int x;
    Foo(int _x);

};

Foo::Foo(int _x){
    x = _x;
}

int main(int argc, char** argv) {
    int n = atoi(argv[1]);
    Foo** gx = new Foo*[n];
    for(int i=0;i<n;i++){
        gx[i] = new Foo(i);
    }
    return 0;
}

Нет флагов компиляции.

компиляция и запуск:

>dmd td.d
>time ./td 10000000
>real   0m2.544s

Пример Anlogue в C ++ (gcc), запуск:

>time ./tc 10000000
>real   0m0.523s

Почему? Такой простой пример и такая большая разница: 2,54 с и 0,52 с.

Ответы [ 2 ]

4 голосов
/ 03 апреля 2013

В основном вы измеряете три различия:

  1. Разница между кодом, сгенерированным gcc и dmd
  2. Дополнительное время D требуется для выделения с помощью GC.
  3. Дополнительное время D занимает выделение класса.

Теперь вы можете подумать, что точка 2 недействительна, потому что вы использовали GC.disable();, но это только делает так, что GC выиграл 'собирать как обычно.Это не заставляет GC полностью исчезать и автоматически перенаправляет все выделения памяти в malloc Си.Он по-прежнему должен делать большую часть того, что он обычно делает, чтобы GC знал о выделенной памяти и обо всем, что требует времени.Обычно это относительно незначительная часть выполнения программы (даже игнорируя преимущества, которые дает GC).Однако ваш эталонный тест делает программу полностью преувеличивающей этот эффект.

Поэтому я предлагаю вам рассмотреть два изменения в вашем подходе:

  1. Либо перейдите на использование gdc для сравненияпротив gcc или переключитесь на dmc для сравнения с dmd
  2. Сделайте программы более эквивалентными.Либо используйте D и C ++ структуры размещения в куче, либо, по крайней мере, сделайте так, чтобы D выделял, не касаясь GC.Если вы оптимизируете программу для максимальной скорости, вы все равно будете использовать структуры и malloc C. Независимо от языка.

Я бы даже рекомендовал 3-е изменение: так как вы заинтересованы вМаксимальная производительность, вы должны попытаться придумать лучшую программу полностью.Почему бы не перейти к структурам и расположить их непрерывно в памяти?Это сделало бы выделение (которое, по сути, всей программы) максимально быстрым.

Использование приведенного выше кода, работающего с использованием dmd & dmc на моей машине, приводит к следующим результатам:

  • DMC 8,42n (без флагов): ~ 880 мс
  • DMD 2.062 (без флагов): ~ 1300 мс

Изменение кода на следующее:

Код C ++:

#include <cstdlib>
struct Foo {
    int x;
};

int main(int argc, char** argv) {
    int n = atoi(argv[1]);
    Foo* gx = (Foo*) malloc(n * sizeof(Foo));
    for(int i = 0; i < n; i++) {
        gx[i].x = i;
    }
    free(gx);
    return 0;
}

Код D:

import std.conv;
struct Foo{
    int x;
}

void main(string args[]) {
    int n = to!int(args[1]);
    Foo[] m = new Foo[](n);
    foreach(i, ref e; m) {
        e.x = i;
    }
}

Использование моего кода с использованием DMD и DMC приводит к следующим результатам:

  • DMC 8,42n(без флагов): ~ 95 мс + - 20 мс
  • DMD 2.062 (без флагов): ~ 95 мс + - 20 мс

По сути, идентично (мне придется начать использовать некоторую статистикучтобы дать вам лучшее представление о том, какой из них действительно быстрее, но в этом масштабе это не имеет значения).Обратите внимание, что использование этого намного, намного быстрее, чем наивный подход, и D в равной степени способен использовать эту стратегию.В этом случае разница во время выполнения незначительна, но мы сохраняем преимущества использования GC, и при написании кода D определенно гораздо меньше проблем, которые могут пойти не так (обратите внимание, что вашей программе не удалось delete всеего распределения?).

Кроме того, если вы абсолютно хотите, D позволяет вам использовать стандартную библиотеку C на import std.c.stdlib; Это позволит вам действительно обойти GC и достичь максимальной производительности с помощью malloc C, еслинеобходимо.В этом случае в этом нет необходимости, поэтому я допустил ошибку в сторону более безопасного и удобочитаемого кода.

2 голосов
/ 08 мая 2011

попробуйте это:

import std.stdio, std.conv, core.memory;

class Foo{
    int x = void;
    this(in int _x){x=_x;}
}

void main(string args[]) {
    GC.disable();
    int n = to!int(args[1]);
    Foo[] m= new Foo[n];
    foreach(i; 0..n){
    m[i] = new Foo(i);
    }
}
...