Здравствуйте, alexzzzz, Вы писали:
I>> Это другой тест, он показыавет, какая из библиотечных функций качественнее сделана. I>>А предыдущий показывал какой из компилеров даёт более качественный код.
A>Это тот же самый тест, решающий ту же самую задачу — сгенерировать из ничего упорядоченную коллекцию. На исполняемый код насрать. Он нужен, чтобы решить задачу, больше он ни зачем не нужен. Чем кода меньше, чем он быстрее работает, тем всегда лучше.
У нас были вполне конкретные данные — объекты со строковыми пропертями. Это принципиальная часть. А вот что именно внутри этих строк — дело десятое. Вместо генератора рандомных ФИО был взят гуид, это уже детали реализации.
I>>Ага, ты сравниваешь гуиды. Так ? То есть, вместо "объект со строковыми филдами" ты влупил другой тест "объект с гуидами" I>>То есть, ты подменяешь яблоки грушами.
A>Я вижу, что на входе программы ничего нет, а на выходе отсортированные гуиды.
I>>Оригинальный код принимает на вход данные в виде массива объектов со строковыми филдами. I>>Ты поменял данные в пользу C# версии.
A>Оригинальный код измеряет время генерирования данных и их последующей обработки. Мы же смотрим на один и тот же код? На входе программы данных нет, на выходе как бы есть.
Ты как то выборочно прочитал сообщения.
I>>В тестах принято входные данные не изменять, не подгонять под нужный результат. A>Входных данных нет. Время генерирования данных включено в измерения.
Ога, ты выбрал какую то более удобную часть. Гораздо интереснее зафиксировать входные данные для сортировки — объекты со строковыми филдами, и сам алгоритм. Вот тогда сравнение обретает смысл.
В этом случае получаем именно разницу между языками по перформансу компилированого кода.
То есть, идея в отделении мух от котлет — качество компилеров отдельно, качество библиотек отдельно, качество веб-фремворков — отдельно.
Результаты же твоего теста вообще непонятно как интерпретировать.
Убрал два косяка предыдущей версии (на скорость не влияли) и ещё ускорил. Пробовал заменить TestClass на TestStruct, но тут это ничего не даёт, разве что чуть экономит память. Ещё воткнул GC.Collect между итерациями, т.к. надоело видеть первую итерацию самой быстрой.
C#
using System;
using System.Diagnostics;
using System.Threading;
namespace QuickSort
{
public class Program
{
private static int activeThreadCount;
private static ManualResetEvent waitHandle = new ManualResetEvent(false);
public static void Main(string[] args)
{
const int ARRAY_SIZE = 1000 * 1000;
const int THREADS = 4;
var watch = new Stopwatch();
for (var i = 0; i < 5; i++)
{
GC.Collect();
watch.Restart();
waitHandle.Reset();
var vals = new TestClass[ARRAY_SIZE];
activeThreadCount = 4;
for (var j = 0; j < THREADS; j++)
{
int segmentIndex = j;
ThreadPool.QueueUserWorkItem(_ => Fill(vals, segmentIndex * (ARRAY_SIZE / THREADS), ARRAY_SIZE / THREADS));
}
waitHandle.WaitOne();
watch.Stop();
Console.WriteLine($"Total: {watch.ElapsedMilliseconds} ms");
}
Console.WriteLine($"GC: {GC.CollectionCount(0)} {GC.CollectionCount(1)} {GC.CollectionCount(2)}");
Console.ReadLine();
}
private static void Fill(TestClass[] array, int start, int count)
{
int end = start + count;
for (int i = start; i < end; i++)
{
array[i] = new TestClass();
}
Interlocked.Decrement(ref activeThreadCount);
if (activeThreadCount == 0)
{
waitHandle.Set();
}
}
}
internal class TestClass
{
public string Id = CreateGuid();
public string Value = CreateGuid();
private static ThreadLocal<Random> localRandom = new ThreadLocal<Random>(() => new Random());
public override string ToString() => Id;
private static unsafe string CreateGuid()
{
var num = localRandom.Value.Next(100_000_000);
var chars = stackalloc char[11]; // null-terminated 10-char string
chars[0] = '0';
chars[1] = '0';
chars[2] = (char)('0' + num % 10); num /= 10;
chars[3] = (char)('0' + num % 10); num /= 10;
chars[4] = (char)('0' + num % 10); num /= 10;
chars[5] = (char)('0' + num % 10); num /= 10;
chars[6] = (char)('0' + num % 10); num /= 10;
chars[7] = (char)('0' + num % 10); num /= 10;
chars[8] = (char)('0' + num % 10); num /= 10;
chars[9] = (char)('0' + num % 10);
return new string(chars);
}
}
}
Total: 195 ms
Total: 177 ms
Total: 179 ms
Total: 169 ms
Total: 189 ms
GC: 113 46 13
Здравствуйте, Ikemefula, Вы писали:
I>У нас были вполне конкретные данные — объекты со строковыми пропертями. Это принципиальная часть. А вот что именно внутри этих строк — дело десятое. Вместо генератора рандомных ФИО был взят гуид, это уже детали реализации.
Ну и у меня внутри гуид вместо строки — детали реализации. Никто не запрещает в JS сделать так же. Не нравится — пиши чёткое ТЗ и давай готовые тестовые данные на входе в виде файла.
I>На выходе тож ничего нет. Ты же их никуда не пишешь
Значит, ты понимаешь, что я чувствую, смотря на оригинальный код.
I>Твой "оптимизированый" тест даёт заранее известный результат: http://rsdn.org/forum/flame.comp/6901419.1
Естественно в C# больше простора для манёвров, я не собираюсь его искусственно тормозить. Какой смысл?
I>То есть, идея в отделении мух от котлет — качество компилеров отдельно, качество библиотек отдельно, качество веб-фремворков — отдельно.
Чтобы что? Ты всё равно не сможешь взять C#-библиотеки, JS-фреймворк, С++-компилятор и соединить.
Здравствуйте, alexzzzz, Вы писали:
I>>У нас были вполне конкретные данные — объекты со строковыми пропертями. Это принципиальная часть. А вот что именно внутри этих строк — дело десятое. Вместо генератора рандомных ФИО был взят гуид, это уже детали реализации.
A>Ну и у меня внутри гуид вместо строки — детали реализации. Никто не запрещает в JS сделать так же. Не нравится — пиши чёткое ТЗ и давай готовые тестовые данные на входе в виде файла.
У тебя совсем другой тест и показывает он разницу между платформами(.net — node, .net — browser, .net — xxx.js.engine и тд), а не качество компилируемого кода.
А вот если зафиксировать тип данных и алгоритм, всё становится очень ясно — результатом теста можно пользоваться для оценки производительности.
Если задачу поставить иначе, например сколько можно выжать из _дотнета_ и _v8_ на одних и тех же задачах, это совсем другой подход. Но тогда и JS вариант так же придётся переписать. Ты же не думаешь, что JS вариант идеален ?
Здравствуйте, Ikemefula, Вы писали:
I>У тебя совсем другой тест и показывает он разницу между платформами(.net — node, .net — browser, .net — xxx.js.engine и тд), а не качество компилируемого кода.
Это абстрактное качество без остальной платформы не интересно.
I>А вот если зафиксировать тип данных и алгоритм, всё становится очень ясно — результатом теста можно пользоваться для оценки производительности.
Нельзя. Без остальной платформы смысла нет.
I>Если задачу поставить иначе, например сколько можно выжать из _дотнета_ и _v8_ на одних и тех же задачах, это совсем другой подход.
Единственно правильный подход. Остальные бессмысленны.
I>Ты же не думаешь, что JS вариант идеален ?
Не думаю. Но не берусь его улучшать, потому что не представляю, как JS работает под капотом.
О, ну вот теперь всё стало на свои места. Все числа на разных машинах пропорциональны друг другу с одним коэффициентом (в нашем случае лишь слегка отличающимся от единицы), как и положено при корректных тестах. Люблю когда всё выясняется до конца и нет никакой подозрительной "магии".
_>>C++ (gcc 6.3.0) A>Я где-то пропустил исходники на С++?
Не, это я тут для собственной оценки (чтобы понять масштаб отставания обсуждаемых платформ от максимальной оптимизации) накидал за пару минут, запустил и выложил за одно в общем итоге. ) К теме дискуссии (во всяком случае реальной, а не той, что в заголовке) это отношения не имеет. )
Здравствуйте, alex_public, Вы писали:
_>>>C++ (gcc 6.3.0) A>>Я где-то пропустил исходники на С++? _>Не, это я тут для собственной оценки (чтобы понять масштаб отставания обсуждаемых платформ от максимальной оптимизации) накидал за пару минут, запустил и выложил за одно в общем итоге. ) К теме дискуссии (во всяком случае реальной, а не той, что в заголовке) это отношения не имеет. )
Я просто увидел время 71 мс и захотел на строки в С++ посмотреть. Что там, char[11] или что-то продвинутое? С полями в виде фиксированных строк C# можно ещё в несколько раз разогнать. Получилось 56 мс в четырёх потоках или 82 мс в одном. Но так как выходной формат данных не определён, непонятно, считается такой вариант или нет.
Вообще, конечно, надо от всех тестов требовать результаты писать в файл, а не оставлять висеть в воздухе с непонятно какой целью. Так и проверять проще, и пространства для манёвра больше, и ни у кого не будет претензий к реализации.
Здравствуйте, alexzzzz, Вы писали:
_>>Не, это я тут для собственной оценки (чтобы понять масштаб отставания обсуждаемых платформ от максимальной оптимизации) накидал за пару минут, запустил и выложил за одно в общем итоге. ) К теме дискуссии (во всяком случае реальной, а не той, что в заголовке) это отношения не имеет. ) A>Я просто увидел время 71 мс и захотел на строки в С++ посмотреть. Что там, char[11] или что-то продвинутое? С полями в виде фиксированных строк C# можно ещё в несколько раз разогнать.
Не, строки там — это обычные std::string, но вот пишем в них мы с помощью (в том варианте, где 71 мс) нестандартного модного инструмента по имени boost::spirit::karma::generate. Это кстати ещё не самый быстрый способ (http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html), но более быстрые уже требуют установки дополнительных библиотек (Boost за дополнительную я не считаю, т.к. это де-факто стандарт в мире C++), что мне уже естественно было лень.
A>Вообще, конечно, надо от всех тестов требовать результаты писать в файл, а не оставлять висеть в воздухе с непонятно какой целью. Так и проверять проще, и пространства для манёвра больше, и ни у кого не будет претензий к реализации.
Ну я на самом деле во всех этих тестах делал вывод последних 10 элементов полученного массива на экран. Ну так, чтобы удостовериться, что всё как надо. )))
Здравствуйте, alex_public, Вы писали:
A>>Вообще, конечно, надо от всех тестов требовать результаты писать в файл, а не оставлять висеть в воздухе с непонятно какой целью. Так и проверять проще, и пространства для манёвра больше, и ни у кого не будет претензий к реализации. _>Ну я на самом деле во всех этих тестах делал вывод последних 10 элементов полученного массива на экран. Ну так, чтобы удостовериться, что всё как надо. )))
Я себе первые три выводил. Но проверка — это больше для самоуспокоения. Два других пункта интереснее.
Кстати, я тут задумался на секунду и понял, что по факту внутри подобного примера у меня реально что-то вроде char[15], но только я ничего подобного не ставил — это у std::string работает SSO (10 символов вполне себе влезают в лимит реализации gcc), про которую разработчик теоретически может быть вообще не в курсе.
Здравствуйте, alexzzzz, Вы писали: A>GC: 113 46 13
Поглядывал периодически на подобные строчки и казались они мне какими-то подозрительными. Что-то слишком много сборок мусора. Раньше такого не наблюдал. Или, может, в Unity отвык.
Короче, добавил в конфиг такую строчку и сразу полегчало:
<gcServer enabled="true"/>
В результате имеем:
C#, один поток
using System;
using System.Diagnostics;
namespace QuickSort
{
public class Program
{
public static void Main()
{
const int ARRAY_SIZE = 1000 * 1000;
var watch = new Stopwatch();
for (var i = 0; i < 5; i++)
{
GC.Collect();
watch.Restart();
var vals = new TestStruct[ARRAY_SIZE];
for (int j = 0; j < vals.Length; j++)
{
vals[j] = TestStruct.Create();
}
watch.Stop();
Console.WriteLine($"Total: {watch.ElapsedMilliseconds} ms");
}
Console.WriteLine($"GC: {GC.CollectionCount(0)} {GC.CollectionCount(1)} {GC.CollectionCount(2)}");
Console.ReadLine();
}
}
internal unsafe struct TestStruct
{
public string id;
public string value;
private static readonly Random random = new Random();
public static TestStruct Create()
{
TestStruct item;
item.id = GenerateRandomNumber();
item.value = GenerateRandomNumber();
return item;
}
public override string ToString() => id;
private static string GenerateRandomNumber()
{
var buffer = stackalloc char[11];
int num = random.Next(100_000_000);
var p = buffer;
*p++ = '0';
*p++ = '0';
*p++ = (char)('0' + num % 10); num /= 10;
*p++ = (char)('0' + num % 10); num /= 10;
*p++ = (char)('0' + num % 10); num /= 10;
*p++ = (char)('0' + num % 10); num /= 10;
*p++ = (char)('0' + num % 10); num /= 10;
*p++ = (char)('0' + num % 10); num /= 10;
*p++ = (char)('0' + num % 10); num /= 10;
*p = (char)('0' + num % 10);
return new string(buffer);
}
}
}
Total: 155 ms
Total: 116 ms
Total: 112 ms
Total: 113 ms
Total: 112 ms
GC: 7 6 6
C#, четыре потока
using System;
using System.Diagnostics;
using System.Threading;
namespace QuickSort
{
public class Program
{
private static int activeThreadCount;
private static ManualResetEvent waitHandle = new ManualResetEvent(false);
public static void Main()
{
const int ARRAY_SIZE = 1000 * 1000;
const int THREADS = 4;
var watch = new Stopwatch();
for (var i = 0; i < 5; i++)
{
GC.Collect();
watch.Restart();
waitHandle.Reset();
var vals = new TestClass[ARRAY_SIZE];
activeThreadCount = THREADS;
for (var j = 0; j < THREADS; j++)
{
int segmentIndex = j;
ThreadPool.QueueUserWorkItem(_ => Fill(vals, segmentIndex * (ARRAY_SIZE / THREADS), ARRAY_SIZE / THREADS));
}
waitHandle.WaitOne();
watch.Stop();
Console.WriteLine($"Total: {watch.ElapsedMilliseconds} ms");
}
Console.WriteLine($"GC: {GC.CollectionCount(0)} {GC.CollectionCount(1)} {GC.CollectionCount(2)}");
Console.ReadLine();
}
private static void Fill(TestClass[] array, int start, int count)
{
int end = start + count;
for (int i = start; i < end; i++)
{
array[i] = new TestClass();
}
Interlocked.Decrement(ref activeThreadCount);
if (activeThreadCount == 0)
{
waitHandle.Set();
}
}
}
internal class TestClass
{
public string id = GenerateRandomNumber();
public string value = GenerateRandomNumber();
private static ThreadLocal<Random> localRandom = new ThreadLocal<Random>(() => new Random());
public override string ToString() => id;
private static unsafe string GenerateRandomNumber()
{
var num = localRandom.Value.Next(100_000_000);
var chars = stackalloc char[11]; // null-terminated 10-char string
chars[0] = '0';
chars[1] = '0';
chars[2] = (char)('0' + num % 10); num /= 10;
chars[3] = (char)('0' + num % 10); num /= 10;
chars[4] = (char)('0' + num % 10); num /= 10;
chars[5] = (char)('0' + num % 10); num /= 10;
chars[6] = (char)('0' + num % 10); num /= 10;
chars[7] = (char)('0' + num % 10); num /= 10;
chars[8] = (char)('0' + num % 10); num /= 10;
chars[9] = (char)('0' + num % 10);
return new string(chars);
}
}
}
Total: 91 ms
Total: 45 ms
Total: 42 ms
Total: 43 ms
Total: 45 ms
GC: 7 6 6
Здравствуйте, alexzzzz, Вы писали:
I>>У тебя совсем другой тест и показывает он разницу между платформами(.net — node, .net — browser, .net — xxx.js.engine и тд), а не качество компилируемого кода.
A>Это абстрактное качество без остальной платформы не интересно.
Между тем, если ты знаешь, что компилер генерит качественный код, то можешь смело начинать пилить целую кучу задач.
I>>А вот если зафиксировать тип данных и алгоритм, всё становится очень ясно — результатом теста можно пользоваться для оценки производительности.
A>Нельзя. Без остальной платформы смысла нет.
Наоборот, платформа не вытянет кривость компилера, как бы ты ни старался.
I>>Если задачу поставить иначе, например сколько можно выжать из _дотнета_ и _v8_ на одних и тех же задачах, это совсем другой подход.
A>Единственно правильный подход. Остальные бессмысленны.
Смотрим techempower. И вот чудо — тормозной node уделывает классный дотнет.
Вопрос — куда потерялся смысл, который ты вкладывал?
Здравствуйте, Ikemefula, Вы писали:
I>>>У тебя совсем другой тест и показывает он разницу между платформами(.net — node, .net — browser, .net — xxx.js.engine и тд), а не качество компилируемого кода. A>>Это абстрактное качество без остальной платформы не интересно. I>Между тем, если ты знаешь, что компилер генерит качественный код, то можешь смело начинать пилить целую кучу задач.
Слышал, что интеловский компилятор Фортрана генерит очень качественный код. Что мне делать с этим знанием?
I>>>А вот если зафиксировать тип данных и алгоритм, всё становится очень ясно — результатом теста можно пользоваться для оценки производительности. A>>Нельзя. Без остальной платформы смысла нет. I>Наоборот, платформа не вытянет кривость компилера, как бы ты ни старался.
Не наоборот, а оно только в комплексе представляет интерес. Много ли ты видел применений Фортрана для чего-нибудь высокоскоростного на веб-серверах или десктопах? А он вроде как multi-paradigm и general-purpose.
I>>>Если задачу поставить иначе, например сколько можно выжать из _дотнета_ и _v8_ на одних и тех же задачах, это совсем другой подход. A>>Единственно правильный подход. Остальные бессмысленны. I>Смотрим techempower. И вот чудо — тормозной node уделывает классный дотнет. I>Вопрос — куда потерялся смысл, который ты вкладывал?
Никуда не потерялся, всё отлично. Если мне критично быстро-быстро отдавать plaintext (просто для примера, т.к. это самое мне понятное из их тестов), то я открываю соответствующий тест и смотрю, как с этим справляются разные фреймворки/языки/платформы. Первым в списке значится некий libreactor. Если у меня и нет аллергии на него и окружение, в котором он работает, а также устраивает функционал, то libreactor — это то, что мне нужно. Пусть он хоть на Бейсике работает.
Здравствуйте, alexzzzz, Вы писали:
A>>>Вообще, конечно, надо от всех тестов требовать результаты писать в файл, а не оставлять висеть в воздухе с непонятно какой целью. Так и проверять проще, и пространства для манёвра больше, и ни у кого не будет претензий к реализации. _>>Ну я на самом деле во всех этих тестах делал вывод последних 10 элементов полученного массива на экран. Ну так, чтобы удостовериться, что всё как надо. ))) A>Я себе первые три выводил. Но проверка — это больше для самоуспокоения. Два других пункта интереснее.
Самое главное-то забыл! Это ещё и железная гарантия, что компилятор не выкинет из кода что-то нужное.
Здравствуйте, alexzzzz, Вы писали:
A>Слышал, что интеловский компилятор Фортрана генерит очень качественный код. Что мне делать с этим знанием?
Будешь смеяться, на на этом фортране до сих пор кучу математики пишут. Думаешь ради чего его оптимизируют ?
I>>>>А вот если зафиксировать тип данных и алгоритм, всё становится очень ясно — результатом теста можно пользоваться для оценки производительности. A>>>Нельзя. Без остальной платформы смысла нет. I>>Наоборот, платформа не вытянет кривость компилера, как бы ты ни старался.
A>Не наоборот, а оно только в комплексе представляет интерес. Много ли ты видел применений Фортрана для чего-нибудь высокоскоростного на веб-серверах или десктопах? А он вроде как multi-paradigm и general-purpose.
А что, если не веб, то всё, жизнь закончилась ? Коммерческий интерес — только в целом. А вот исследуется как правило по отдельности.
I>>Вопрос — куда потерялся смысл, который ты вкладывал?
A>Никуда не потерялся, всё отлично. Если мне критично быстро-быстро отдавать plaintext (просто для примера, т.к. это самое мне понятное из их тестов), то я открываю соответствующий тест и смотрю, как с этим справляются разные фреймворки/языки/платформы. Первым в списке значится некий libreactor. Если у меня и нет аллергии на него и окружение, в котором он работает, а также устраивает функционал, то libreactor — это то, что мне нужно. Пусть он хоть на Бейсике работает.
Так вроде очевидно что сортировка и эти тесты вобщем сравнивают вещи совершенно разные, но для тебя это почему то вариации на одну и ту же тему
Здравствуйте, Ikemefula, Вы писали:
A>>Слышал, что интеловский компилятор Фортрана генерит очень качественный код. Что мне делать с этим знанием? I>Будешь смеяться, на на этом фортране до сих пор кучу математики пишут. Думаешь ради чего его оптимизируют ?
Я знаю, что на нём пишут. Есть Фортран, у которого «компилер генерит качественный код». Что-то не видно, чтобы народ на нём «смело начинал пилить целую кучу задач».
I>Так вроде очевидно что сортировка и эти тесты вобщем сравнивают вещи совершенно разные, но для тебя это почему то вариации на одну и ту же тему
Мы говорим каждый о чём-то своём и друг друга не понимаем вообще. Аминь.
Здравствуйте, Serginio1, Вы писали:
S> На самом деле нужны тесты сравнивающие скорость GC, скорость JIT компиляции и скорость выполнения.
А еще есть скорость загрузки. Меня нода уже задолбала, любая cli команда работает с заметным лагом. Понятно, что это чудище каждый раз грузит кучу модулей, но нельзя как-то закешировать состояние между вызовами? Не для своего кода, а глобально?
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Ops, Вы писали:
Ops>А еще есть скорость загрузки. Меня нода уже задолбала, любая cli команда работает с заметным лагом. Понятно, что это чудище каждый раз грузит кучу модулей, но нельзя как-то закешировать состояние между вызовами? Не для своего кода, а глобально?
Там адъ почище виндовса. В большинстве случаев принято чуть не всю работу делать сторонними либами. Объем кода в депенденсах считается в сотнях мегабайт и больше.
Здравствуйте, Ikemefula, Вы писали:
I>Там адъ почище виндовса. В большинстве случаев принято чуть не всю работу делать сторонними либами. Объем кода в депенденсах считается в сотнях мегабайт и больше.
Так я не хочу побороть этот ад, это вряд ли кому-то по силам, только закешировать его, а то иная утилита только грузится секунду, хотя отрабатывает за десяток миллисекунд.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.