Здравствуйте, gandjustas, Вы писали:
G>Тебе ситуация кажется странной, потому что ты не понимаешь экономических мотивов технологических решений.
Да ладно, какие там экономические мотивы. Переехать с VSS на что-то более приличное — достаточно пары дней максимум.
Это просто множество говнокодеров и говноменеджеров, которые будут сопротивляться любым изменениям.
Здравствуйте, mik1, Вы писали:
M>Меня больше пугают 30% вариативности результатов сишного примера. В Яве там практически нулевое стандартное отклонение.
Я правильно понимаю, что то, что вы написали некорректный код, вас не пугает совсем?
Может вы перемерите производительность корректного кода?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Aртём, Вы писали:
EP>>>>Сложность вставки в середину std::deque — линейная. Aё>>>Это типа ключ к производительности? EP>>Это такая структура данных. Структуры данных бывают разные, и каждая полезна при каких-то определённых условиях Aё>Вы сначала привели std::deque как пример производительности, потом в кусты. Нехорошо.
Это передёргивание чистой воды Я не говорил что вставка в середину deque — быстрая.
Aё>>>Поля объекта вектор для Вас это value type? EP>>Изучай Aё>Гуглить я умею. Вы не ответили на мой вопрос- Вы действительно считаете, что поля объекта data типа vector являются типами по значению для типа Foo? Или таки при копировании Foo для data будет вызван копирующий конструктор?
Открой ссылку и прочитай что такое value type.
EP>>>>Опять таки, то что на Java можно написать быстрый код дорогой ценной — я сказал в том же сообщении. Aё>>>У вас обоих код тормозной и написан за минуту. С тем отличием, у mik1 код без запашка. EP>>Как минимум с тем отличием что у mik1 логическая ошибка, как раз в самом характерном месте. Aё>Не вдавался в подробности, что там нарушил mik1 при перемножении- но сомневаюсь, что дело в адресной арифметике.
Причём тут адресная арифметика? Сам придумал, сам сомневается
Aё>Вообще как-то удивляет, что вроде бы более низкоуровневый программист на C++ боится адресной арифметики в Java и вообще не ожидает такого от Java-иста.
Мой тезис в том что быстрый код на C++ более высокоуровневый чем быстрый код на Java.
EP>>Уже третьи сутки пошли, а корректного кода на Java так и нет. Aё>Дам Вам вопрос на засыпку- напишете без сторонних библиотек элегантный и быстрый код для reverse matrix со стороной N?
Что такое "reverse" matrix? Как это вообще относится к теме?
Ты даже не в состоянии найти баг в реализации mik1, да и вообще твоей версии кода здесь не было — при этом зачем-то просишь написать код для какого-то стороннего примера
EP>>Это вообще ортогонально — в Java можно создать mutable строку, в C++ — immutable Aё>Просто для информации- в Java String immutable из коробки, в C++ std::string mutable из коробки.
Ещё раз — это ортогонально к обсуждаемому вопросу. Лишняя индерекция никуда не делась
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Как минимум с тем отличием что у mik1 логическая ошибка, как раз в самом характерном месте. Писал бы он медленный Java-style с объектами Complex — такой ошибки бы не было. EP>Уже третьи сутки пошли, а корректного кода на Java так и нет.
Тебя мучает, что я одно присваивание неправильно написал? Первым же юнит тестом его бы накрыло. Писал я его в 2 часа ночи по местному времени.
В отличии от тебя я сюда заглядываю, а не сижу тут.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, mik1, Вы писали:
M>>Меня больше пугают 30% вариативности результатов сишного примера. В Яве там практически нулевое стандартное отклонение. S>Я правильно понимаю, что то, что вы написали некорректный код, вас не пугает совсем? S>Может вы перемерите производительность корректного кода?
Сразу после того, как вы в Яве статический конструктор скомпилируете.
public double testVectorMult()
{
double t;
for ( int i = 0; i < u_ar.length / 2; ++i ) {
t = u.re(i) * v.re(i) - u.im(i) * v.im(i);
v.im(i, u.re(i)*v.im(i) + u.im(i)*v.re(i));
v.re(i, t);
}
return u.re( 0 );
}
Здравствуйте, Sinclair, Вы писали:
M>>Меня больше пугают 30% вариативности результатов сишного примера. В Яве там практически нулевое стандартное отклонение. S>Я правильно понимаю, что то, что вы написали некорректный код, вас не пугает совсем? S>Может вы перемерите производительность корректного кода?
Да, так что насчет такого разброса результатов в си? Как-то меня он очень смущает...
Здравствуйте, mik1, Вы писали:
Aё>>>У вас обоих код тормозной и написан за минуту. С тем отличием, у mik1 код без запашка. EP>>Как минимум с тем отличием что у mik1 логическая ошибка, как раз в самом характерном месте. Писал бы он медленный Java-style с объектами Complex — такой ошибки бы не было. EP>>Уже третьи сутки пошли, а корректного кода на Java так и нет. M>Тебя мучает, что я одно присваивание неправильно написал? Первым же юнит тестом его бы накрыло. Писал я его в 2 часа ночи по местному времени.
У тебя баг именно в том месте, которого могло бы и не быть при наличии структур в языке, "кодом без запашка" это точно не является (на что я и отвечал). Писал бы ты в Java-style с полноценным объектом Complex — то этой ошибки бы не было.
Если бы ошибся например с замером времени, или например нарандомил денормализированных чисел — то и вопросов никаких не было бы, так как это к теме совершенно не относится, все ошибаются.
M>В отличии от тебя я сюда заглядываю, а не сижу тут.
А мне нравится обмениваться опытом, не всегда конечно здесь это результативно, но тем не менее попадаются очень интересные дискуссии.
А на что я трачу своё личное время из своего свободного графика — это к теме совершенно не относится. В любом случае код я показал сразу же.
Здравствуйте, Evgeny.Panasyuk, Вы писали: EP>>>>Пока ты редактировал сообщение, я уже сделал в таком виде Убрать зависимость boost конечно не сложно, но давай уже сделаем это после того как увидим Java версию.
Теперь разберем дотнет
Я поменял плюсный код, так как у меня пока нет С++14 компилятора на машине. Еще добавил чтение из файла, чтобы сверить результаты, и они сходятся.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication1
{
struct Complex
{
public Complex(double re, double im)
{
Re = re;
Im = im;
}
public double Re;
public double Im;
public static Complex operator +(Complex x, Complex y)
{
return new Complex(x.Re + y.Re, x.Im + y.Im);
}
public unsafe static Complex operator *(Complex x, Complex y)
{
return new Complex(x.Re * y.Re - x.Im * y.Im, x.Re * y.Im + x.Im * y.Re);
}
private const int RandMax = 32767;
private static Random random = new Random();
public static Complex RandomComplex()
{
return new Complex(random.Next(RandMax) / 1000D, random.Next(RandMax) / 1000D);
}
}
static class Funcs
{
public static void Shuffle<T>(IList<T> list)
{
Random rng = new Random();
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
public static void PopulateWithRandom(Complex[] v, Complex[] u)
{
const int n = 1 << 24;
for (int i = 0; i < n; i++ )
{
Complex random = Complex.RandomComplex();
v[i] = random;
u[i] = random;
}
Funcs.Shuffle(u);
Funcs.Shuffle(v);
}
public static void LoadComplexSequence(Complex[] v, Complex[] u)
{
const int n = 1 << 24;
using (var stm = File.OpenRead(@".\NativeBenchmark\complex.dat"))
{
using (var tr = new StreamReader(stm))
{
foreach(var list in new Complex[][]{v, u})
{
int i = 0;
string line;
while (i < n && (line = tr.ReadLine()) != null)
{
var components = line.Split(' ');
if (components.Length != 2)
throw new ApplicationException("There should be two components");
list[i] = new Complex(Convert.ToDouble(components[0]), Convert.ToDouble(components[1]));
i++;
}
Console.WriteLine("Added {0} elements", i);
}
}
}
}
}
class Program
{
[DllImport("winmm.dll")]
internal static extern uint timeBeginPeriod(uint period);
[DllImport("winmm.dll")]
internal static extern uint timeEndPeriod(uint period);
static void Main(string[] args)
{
const int n = 1 << 24;
Console.WriteLine("n={0}", n);
var v = new Complex[n];
var u = new Complex[n];
var watch = Stopwatch.StartNew();
Funcs.PopulateWithRandom(v, u);
//Funcs.LoadComplexSequence(v, u);
watch.Stop();
Console.WriteLine("Loading time: {0:F4}", watch.ElapsedMilliseconds);
timeBeginPeriod(5);
unsafe
{
watch = Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
v[i] = v[i] * u[i];
}
}
watch.Stop();
timeEndPeriod(5);
var result = new Complex(0D, 0D);
v.ToList().ForEach(c=>{result = result + c;});
Console.WriteLine(".NET Elapsed: {0:F4} ms", watch.ElapsedMilliseconds);
Console.WriteLine("Result: {0}, {1}", result.Re, result.Im);
}
}
}
Замеры:
Просто Elapsed это плюсы.
.NET Elapsed: 179.0000 ms
Elapsed = 94.006 ms
.NET Elapsed: 195.0000 ms
Elapsed = 85.005 ms
.NET Elapsed: 182.0000 ms
Elapsed = 87.005 ms
.NET Elapsed: 215.0000 ms
Elapsed = 95.005 ms
.NET Elapsed: 187.0000 ms
Elapsed = 87.005 ms
.NET Elapsed: 190.0000 ms
Elapsed = 91.006 ms
.NET Elapsed: 191.0000 ms
Elapsed = 95.006 ms
.NET Elapsed: 186.0000 ms
Elapsed = 99.006 ms
.NET Elapsed: 181.0000 ms
Elapsed = 95.006 ms
.NET Elapsed: 192.0000 ms
Elapsed = 97.006 ms
Народ, дайте знать, если можно оптимизировать дотнетный код еще. Я поправлю и замерю.
Здравствуйте, greenpci, Вы писали:
G>Народ, дайте знать, если можно оптимизировать дотнетный код еще. Я поправлю и замерю.
Подключи blas/lapack и замерь: управляемый код, который крутит матрицы в специализированной библиотеке (под Java точно есть обёртки), и C++- самописная реализация. До того, размахивать флагом что C++ тормозит чуть быстрей, чем _подставить по вкусу_, бессмысленно. Я уже упоминал программу Mathematica, стандарт де-факто в узких кругах- кросс-платформа на Java, крутит вычисления в специализированных библиотеках, в том числе может задействовать CUDA.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>У меня разница в десятки процентов. Возможно у тебя что-то не так с окружением. EP>Ты C# версию как запускаешь? Случайно не по "F5"?
Из командной строки, правда из под mingw. Запустил из вижуал студии команд лайн, те же цифры. Си плюс плюс компилировал mingw gcc.
Здравствуйте, greenpci, Вы писали:
G>Теперь разберем дотнет G>Народ, дайте знать, если можно оптимизировать дотнетный код еще. Я поправлю и замерю.
Если говорить про этот цикл:
unsafe
{
watch = Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
v[i] = v[i] * u[i];
}
}
watch.Stop();
то всё ок там, нечего оптимизировать
Не, можно заиспользовать RyuJIT+SIMD или распараллелить *(конкретно тут не поможет, время слишком мелкое), но это читерство и несерьёзно.
UPD. Ещё, обычно рандом инициализируют константой,
Random rng = new Random(0);
, чтобы повторяемые результаты были. Опять-таки это скорее вопрос для собеседования, чем смертельно важный факт
Такая просьба: запусти дотнетный код с Target platform = x86. Текущий JIT под x64 любит на ровном месте кривой код генерить. У меня соотношение 58ms/116ms в пользу x86.
Да, можно вытащить в отдельный метод
unsafe private static void Run(int n, Complex[] v, Complex[] u)
{
for (int i = 0; i < n; i++)
{
v[i] = v[i] * u[i];
}
}
— это уменьшит ограничение времени на JIT и даст возможность сгенерить чуть-чуть лучший код, но это копейки: 60 vs 58 ms.
UPD2. Тот же код с классами вместо структур:
.NET Elapsed: 4723,0000 ms
Наглядная иллюстрация gc pressure и золотого правила "ежели один человек построил, другой завсегда разобрать может"
UPD3.
На той же машине плюсовый код от ув. greenpci (vs2013, релиз, win32, без отладчика) на настройках проекта по умолчанию выдаёт
Здравствуйте, Sinix, Вы писали:
S>Такая просьба: запусти дотнетный код с Target platform = x86. Текущий JIT под x64 любит на ровном месте кривой код генерить. У меня соотношение 58ms/116ms в пользу x86.
да, отличный совет! Вот он секрет низких процентов у Евгения.
.NET Elapsed: 107.0000 ms
Elapsed = 98.005 ms
.NET Elapsed: 115.0000 ms
Elapsed = 96.006 ms
.NET Elapsed: 112.0000 ms
Elapsed = 89.005 ms
.NET Elapsed: 111.0000 ms
Elapsed = 96.005 ms
.NET Elapsed: 114.0000 ms
Elapsed = 96.005 ms
.NET Elapsed: 108.0000 ms
Elapsed = 94.005 ms
.NET Elapsed: 106.0000 ms
Elapsed = 90.005 ms
.NET Elapsed: 113.0000 ms
Elapsed = 94.006 ms
.NET Elapsed: 117.0000 ms
Elapsed = 99.005 ms
.NET Elapsed: 111.0000 ms
Elapsed = 89.005 ms
S>Да, можно вытащить в отдельный метод S>
S> unsafe private static void Run(int n, Complex[] v, Complex[] u)
S> {
S> for (int i = 0; i < n; i++)
S> {
S> v[i] = v[i] * u[i];
S> }
S> }
S>
S>- это уменьшит ограничение времени на JIT и даст возможность сгенерить чуть-чуть лучший код, но это копейки: 60 vs 58 ms.
это практически ничего не изменило:
.NET Elapsed: 114.0000 ms
Elapsed = 92.005 ms
.NET Elapsed: 118.0000 ms
Elapsed = 94.005 ms
.NET Elapsed: 114.0000 ms
Elapsed = 95.005 ms
.NET Elapsed: 118.0000 ms
Elapsed = 102.006 ms
.NET Elapsed: 117.0000 ms
Elapsed = 95.005 ms
.NET Elapsed: 117.0000 ms
Elapsed = 93.006 ms
.NET Elapsed: 114.0000 ms
Elapsed = 95.005 ms
.NET Elapsed: 118.0000 ms
Elapsed = 98.006 ms
.NET Elapsed: 116.0000 ms
Elapsed = 93.005 ms
.NET Elapsed: 113.0000 ms
Elapsed = 93.005 ms
S>UPD2. Тот же код с классами вместо структур: S>
S>.NET Elapsed: 4723,0000 ms
S>
А вот List вместо массивов, ради интереса.
.NET Elapsed: 470.0000 ms
Elapsed = 93.0053 ms
.NET Elapsed: 475.0000 ms
Elapsed = 94.0053 ms
.NET Elapsed: 474.0000 ms
Elapsed = 98.0057 ms
.NET Elapsed: 490.0000 ms
Elapsed = 98.0056 ms
.NET Elapsed: 453.0000 ms
Elapsed = 100.006 ms
.NET Elapsed: 472.0000 ms
Elapsed = 93.0053 ms
.NET Elapsed: 471.0000 ms
Elapsed = 89.0051 ms
.NET Elapsed: 468.0000 ms
Elapsed = 94.0054 ms
.NET Elapsed: 456.0000 ms
Elapsed = 91.0052 ms
.NET Elapsed: 483.0000 ms
Elapsed = 95.0054 ms
S>Наглядная иллюстрация gc pressure и золотого правила "ежели один человек построил, другой завсегда разобрать может"
У меня был обратный опыт. Один деятель на работе решил, что если методов нет, то надо поменять class на struct. Он попросил меня помочь разобраться, почему у него чертовщина с разными значениями полей в разных местах начала твориться. Типа, вот смотри я поменял значение в методе, а в вызывающем методе они опять старые.
Здравствуйте, Sinix, Вы писали: S>Не, можно заиспользовать RyuJIT+SIMD
От SIMD тут будет не так много эффекта, так как значительную часть времени здесь занимает перекачка данных из памяти. Точнее он будет, но не кратен увеличению max flop/s.
GCC кстати делает векторизацию C++ кода выше автоматом (опции компиляции):
S>или распараллелить *(конкретно тут не поможет, время слишком мелкое)
Распараллеливание существенно помогает даже на Coliru.
Причём распараллеливается двумя строчками — добавлением #include <parallel/algorithm> и заменой std::transform на __gnu_parallel::transform (у него такая же сигнатура) S>но это читерство и несерьёзно.
Пример же совершенно не про SIMD и Parallel. Как я уже говорил выше — отсутствие структур и лишние индерекци бьют далеко не только по интенсивным вычислениям, а практически по всему коду. S>UPD2. Тот же код с классами вместо структур: S>
S>.NET Elapsed: 4723,0000 ms
S>
Я об этом и говорил выше — разница может быть на порядки. S>Наглядная иллюстрация gc pressure и золотого правила "ежели один человек построил, другой завсегда разобрать может"
А дело тут не в GC, а в постоянных cache miss'ах. Если на C++ сделать такой же memory layout — то точно также получим тормоза (подобный пример
Здравствуйте, greenpci, Вы писали:
G>Вот он секрет низких процентов у Евгения.
Ну вот, ложечки нашлись. Вообще, безобразие с двумя версиями JIT всех, кто работает с числомолотилками порядком достало, особенно после таких вот багов
.
Ждём следующего релиза (который не 2015, а после него), наконец обещают общий код для всех целевых платформ. Хоть баги будут одинаковые, и то хлеб.
S>>Наглядная иллюстрация gc pressure и золотого правила "ежели один человек построил, другой завсегда разобрать может"
G>У меня был обратный опыт. Один деятель на работе решил, что если методов нет, то надо поменять class на struct. Он попросил меня помочь разобраться, почему у него чертовщина с разными значениями полей в разных местах начала твориться. Типа, вот смотри я поменял значение в методе, а в вызывающем методе они опять старые.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А дело тут не в GC, а в постоянных cache miss'ах. Если на C++ сделать такой же memory layout — то точно также получим тормоза (подобный пример
А вот List, похоже, тормозит из-за проверки индекса, которую нельзя убрать, с помощью unsafe
public T this[int index] {
#if !FEATURE_CORECLR
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
get {
// Following trick can reduce the range check by oneif ((uint) index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
Contract.EndContractBlock();
return _items[index];
}
Кстати, ты плюсный код 13й студией компилировал? У меня нету на машине, не могу проверить если есть разница.
Здравствуйте, greenpci, Вы писали:
EP>>А дело тут не в GC, а в постоянных cache miss'ах. Если на C++ сделать такой же memory layout — то точно также получим тормоза (подобный пример
). G>А вот List, похоже, тормозит из-за проверки индекса, которую нельзя убрать, с помощью unsafe
Не думаю что из-за проверки индекса будет такая разница — branch predictor должен с ней неплохо справится. Желательно посмотреть какой там ASM получается.
На MSVC++ если использовать std::vector::at, внутри которого такая же проверка, то там разница на десятки процентов а не в разы.
G>Кстати, ты плюсный код 13й студией компилировал? У меня нету на машине, не могу проверить если есть разница.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Не думаю что из-за проверки индекса будет такая разница — branch predictor должен с ней неплохо справится. Желательно посмотреть какой там ASM получается.
+1. У меня разница между массивом и списком 60ms/88ms.
EP>На MSVC++ если использовать std::vector::at, внутри которого такая же проверка, то там разница на десятки процентов а не в разы.
Кстати, а есть возможность проверить плюсовый код отсюда
с дефолтными настройками проекта (console application)?
У меня какая-то ересь получается.
VS2013, релиз, win32, без отладчика на настройках проекта по умолчанию выдаёт
Elapsed = 189.011 ms
Чтоб собралось без особых вмешательств — убрал constexpr, отключил precompiled headers в настройках и убрал load_complex_sequence() (всё равно не используется).
Здравствуйте, Sinix, Вы писали:
EP>>Не думаю что из-за проверки индекса будет такая разница — branch predictor должен с ней неплохо справится. Желательно посмотреть какой там ASM получается. S>+1. У меня разница между массивом и списком 60ms/88ms.
Это хорошо.
EP>>На MSVC++ если использовать std::vector::at, внутри которого такая же проверка, то там разница на десятки процентов а не в разы. S>Кстати, а есть возможность проверить плюсовый код отсюда
с дефолтными настройками проекта (console application)? S>У меня какая-то ересь получается. S>VS2013, релиз, win32, без отладчика на настройках проекта по умолчанию выдаёт S>
S>Elapsed = 189.011 ms
S>
S>Чтоб собралось без особых вмешательств — убрал constexpr, отключил precompiled headers в настройках и убрал load_complex_sequence() (всё равно не используется).
Я сделал тоже самое MSVS2012 — у меня на Sandy Bridge C++ 60ms (C# 69ms), на Nehalem C++ 84ms (C# 88ms)
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Я сделал тоже самое MSVS2012 — у меня на Sandy Bridge C++ 60ms (C# 69ms), на Nehalem C++ 84ms (C# 88ms)
Sandy bridge тоже. Значит, проблема или в vs2013, или в /dev/hands. Второе вероятнее