В основном вы измеряете три различия:
- Разница между кодом, сгенерированным gcc и dmd
- Дополнительное время D требуется для выделения с помощью GC.
- Дополнительное время D занимает выделение класса.
Теперь вы можете подумать, что точка 2 недействительна, потому что вы использовали GC.disable();
, но это только делает так, что GC выиграл 'собирать как обычно.Это не заставляет GC полностью исчезать и автоматически перенаправляет все выделения памяти в malloc Си.Он по-прежнему должен делать большую часть того, что он обычно делает, чтобы GC знал о выделенной памяти и обо всем, что требует времени.Обычно это относительно незначительная часть выполнения программы (даже игнорируя преимущества, которые дает GC).Однако ваш эталонный тест делает программу полностью преувеличивающей этот эффект.
Поэтому я предлагаю вам рассмотреть два изменения в вашем подходе:
- Либо перейдите на использование gdc для сравненияпротив gcc или переключитесь на dmc для сравнения с dmd
- Сделайте программы более эквивалентными.Либо используйте 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, еслинеобходимо.В этом случае в этом нет необходимости, поэтому я допустил ошибку в сторону более безопасного и удобочитаемого кода.