Re[5]: C# generics: Тормоза. Что я не так делаю?
От: TK Лес кывт.рф
Дата: 12.01.05 21:45
Оценка: 21 (1) +1 :)
Hello, "Павел Кузнецов"

> Для меня больше вопрос не в абсолютных цифрах C# vs. C++, а в относительных: насколько падает быстродействие C# для арифметики и около того при использовании мало-мальской абстракции над встроенными типами и попытках повторного использования кода. Пока результаты не радуют.

>

Два раза это так плохо? Если зависимость линейная и проект на год/полтора, то можно все на intel списать
Плюс, обычно оптимизировать нужно не все приложение, а лишь небольшую его часть — эту часть вполне можно сделать на управляемой версии C++
Posted via RSDN NNTP Server 1.9
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[6]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 13.01.05 04:33
Оценка: 3 (2) -1
TK,

> Два раза это так плохо?


В основном — не очень. Иногда — плохо. Легко можно представить, что может означать уменьшение FPS в два раза для какого-нибудь видеоплеера. (*)

> Если зависимость линейная и проект на год/полтора, то можно все на intel списать Плюс, обычно оптимизировать нужно не все приложение, а лишь небольшую его часть — эту часть вполне можно сделать на управляемой версии C++


Ключевое слово — "если" К тому же, чтобы можно было все списать, имхо, зависимость должна быть константной, а не линейной, т.к. линейная будет означать все большее и большее замедление по мере усложнения.

Мне как раз и интересно, какая там зависимость "штрафов" от повышения уровня абстракции и повторного использования кода (**), и насколько легко оптимизировать тормозящие части. Далеко не всегда можно легко переписать часть приложения на другом языке: из этих оптимизируемых частей запросто может потребоваться вызывать остальные части, что легко может повлечь дублирование ощутимых кусков, т.к., например, пересечение границы managed/unmanaged, насколько я понимаю, тоже далеко не бесплатно.

Кроме того, мне интересны последствия решений, принятых относительно дизайна generics. Например, одно из далеко не всегда приятных последствий — необходимость в модификации generics, которые используют другие generics, при добавлении новых ограничений на аргументы последних. Это в некотором роде можно рассматривать как нарушение инкапсуляции, т.к. все generic пользователи generic сущностей вынужденно "знают" о потребностях последних.

Соответственно, пока что мне интересны варианты обхода тормозов и проблем с повторным использованием кода именно в рамках C#.



(*) Disclaimer для особо горячих товарищей: это не означает, что generics/.Net/C#/whatever будут автоматически замедлять все в два раза, а только лишь является суждением о том, плохо ли само по себе замедление в 2 раза

(**) Прошу не смешивать с повторным использованием компонентов.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.01.05 15:40
Оценка: :)))
Здравствуйте, Serginio1, Вы писали:

S> В данном коде никакого инлайна не будет (веренее теле инлайна будет вызов l.CompareTo(r) вместо реальног сравнения) . Опять наступаешь на теже грабли.

S> Заметь для инта у тебя сразу скорость упала

+1

S>Вот любители перегрузок. Вызов виртуальных (не инлайновых) компараторов не смущает. Или долго сделать специализацию компараторов конкретного типа, исключая виртуальность и используя инлайн????

S> Перегрузку операторов тебе не лень писать. Зачем такие сложности

Гы. Думаю что он ради этого эти тесты и писал. Хотел убедиться, что пока у С++ кое-какие приемущества по оптимизации остались. Возможно даже малость в душе покоробило, что разница не ужасно большая.
... << RSDN@Home 1.1.4 beta 3 rev. 273>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.01.05 23:53
Оценка: 15 (1) :)
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>В основном — не очень. Иногда — плохо. Легко можно представить, что может означать уменьшение FPS в два раза для какого-нибудь видеоплеера. (*)


Да откуда ты взял понижение FPS в два раза? На основании того что листья желтые сделал предположение о скором конце света? Выбрал редковстречающийся и самый сложный для оптимизации сценарий и сделал на его базе выводы об общей производительности. В 99% разницы не вооруженным взглядом ты не заметишь. Например, воспользуйся в своем С++-ном тесте С-шной функций qsort и погляди на результат. Что на базе их можно сделать, что С нельзя использовать для высокопроизводительных программ?

ПК>Ключевое слово — "если" К тому же, чтобы можно было все списать, имхо, зависимость должна быть константной, а не линейной, т.к. линейная будет означать все большее и большее замедление по мере усложнения.


Блин, зависимость такая. 2 раза можно получить при самх наихудших условиях. Ну, если конечно не воспользоваться готовым тормозным кодом. Обычно проигрыш колеблится в раене от 5-20 процентов. А это вообще не заметно на гляз. Более того я тебе уже приводил случаи когда при портировании 3D-движка с С++ на C# он стал быстрее. Так что кончай заниматься самовнушением.

ПК>Мне как раз и интересно, какая там зависимость "штрафов" от повышения уровня абстракции и повторного использования кода (**),


Зависит от абстракции. Ты измеряешь скорость косвенного вызова по отношению к скорости прямого или вообще заинлайненного. Тут как никрути разница будет всегда нехилой. Особенно с VC который и устранять виртуальность умеет, и инлайнинг делает на раз. А ты вот с Борландом сравни. У него с этим тоже не здорово.

ПК> и насколько легко оптимизировать тормозящие части.


Блин, ну, напиши версию без компареров (специализированную) и увидишь, что разницы почти нет. Если тебе нужен именно сортировка, то можешь создать МС++-проект и на рем реализовать нужный шаблон, а потом обернуть его специализации в менеджед-обертку. Прокт просто подключить к проекту на Шарпе и использовать как библиотеку.

ПК> Далеко не всегда можно легко переписать часть приложения на другом языке: из этих оптимизируемых частей запросто может потребоваться вызывать остальные части, что легко может повлечь дублирование ощутимых кусков, т.к., например, пересечение границы managed/unmanaged, насколько я понимаю, тоже далеко не бесплатно.


Нет проблем в вызове одного из другого (это тебе не длл с их проблемами). Да и про проблемы скорости это ты явно преувеличиваешь. Пара ассемблерных команд очень редко является решающей в большом проекте. Скорее решающим будет выбор стратегии или алгоритма. Твой же тест это именно тест на то кто уберет пару лишних инструкций.

Кстатит, по хорошему чтобы понять что происходит нужно получить ассемблерные листинги и сравнить их.

ПК>Кроме того, мне интересны последствия решений, принятых относительно дизайна generics. Например, одно из далеко не всегда приятных последствий — необходимость в модификации generics, которые используют другие generics, при добавлении новых ограничений на аргументы последних.


Это скорее последствие бездарного дизайна. Дженерики делаются не для использования в дженериках (это в конце концов не макросы), а для реализации неких обобщенных алгоритмов и контейнеров. Про метапрограммирование на дженериках нужно сразу же забывать. Это просто не пройдет. Их специально ограничели.

ПК> Это в некотором роде можно рассматривать как нарушение инкапсуляции, т.к. все generic пользователи generic сущностей вынужденно "знают" о потребностях последних.


Чушь это. Дженирики явно требуют от своих потребителей, чтобы те передавали им в качестве параметров типы обладающие неким интерфейсом. Тоже самое, что требует С от потребителей функций. Другими словами — это требования типобезопастности. И не нужно все выворачивать на изнанку. Никакого "знания" о "потребностях" нет и в помини. Есть строгая типизация для всего. И огромное удобство использования — контроль при компиляции и комплит ворд.

ПК>Соответственно, пока что мне интересны варианты обхода тормозов и проблем с повторным использованием кода именно в рамках C#.


Да не надо обходить то чего нет. Менеджед код компилируется в нэйтив код. Есть пара набор операций который более дорог нежели в анменеджед мире. Но есть и более дешевый. Из дорогого:
1. Вызов делегатов в 2-3 дороже виртуального вызова (ранее было в 20-40 раз).
2. Модификация ссылочных полей медленее примерно в два-три раза за счет writw-барьера (специального кода отслеживающего ссылки из старых поколений и ускоряющего ЖЦ). Во втором фрэймворке встроены нехилые оптимизации удаляющие райт барьер для массивов и нвых объектов (пока что только внутри функции).
3. Вызво методов интерфейсов в 1.5 раза медленее обычного виртуального вызвоа. Это следствие довольно дурацкой реализации вызва метода интерфейса (с тройной косвинностью).
4. Приведение типов дороже чем стати-касты в С++. Их цена где-то сравнима с динамик-кастом (чуть быстрее чем динамик-каст).
5. Работа с финализируемыми объектами.
6. Есть неявнеы проверки выхода за границу массива. Но это фигня, так как подобные вещи выносятся из циклов и очень быстры.

Из того что быстрее:
1. Динамическое выделение памяти.
2. Приведение к интерфейсом (его по сути нет).
3. Освобождение памяти (его тоже нет). Вместо него живые объекты копируется в более старое поколение.


Все остальное приблизительно сравнимо с мнменеджед-кодом и является вопросом качества оптимизатора.

ПК>

ПК>(*) Disclaimer для особо горячих товарищей: это не означает, что generics/.Net/C#/whatever будут автоматически замедлять все в два раза, а только лишь является суждением о том, плохо ли само по себе замедление в 2 раза

Обясняю для особо въедливых тваришей. Нет по жизни никакого двухкратного замедления. Как в прочем и кода похожего на втой тест.

Ну, и оптять же понятно, что все это вопрос качества оптимизации. О том, что над оптимизатором работы ведутся, ты знаешь и сам. Так что попрос не стоит даже обсуждения. Нужно дождаться релиза, а то и выхода Феникса.

ПК>(**) Прошу не смешивать с повторным использованием компонентов.


Ну, то-есть повторное использование бывает только на макросоподобной основе? А если я пользуюсь ООП, то это уже не кошерно и очень медленно?
... << RSDN@Home 1.1.4 beta 3 rev. 279>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: C# generics: Тормоза. Что я не так делаю?
От: Tom Россия http://www.RSDN.ru
Дата: 13.01.05 17:08
Оценка: :))
Ээээ. Паш а ты теперь будешь там на C#-пе писать? С плюсы как же? Не покидай нас!
Народная мудрось
всем все никому ничего(с).
Re[3]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.01.05 23:53
Оценка: -2
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Пока планов писать что-нибудь кроме тестов на C# у меня нет (разве что, если в web какой-нить занесет). (По крайней мере пока) к C# у меня интерес исключительно академический. Интересно смотреть на все (новые) языки. Просто, наконец, получилось немного с C# generics поиграться, а не только в черновиках стандарта читать. Соответственно, и вопросы появились практического характера.


Забавно, что совсем необоснованых суждений стало мнеьше. Теперь ты уже в основном напираешь на скорость. То ли еще будет.
... << RSDN@Home 1.1.4 beta 3 rev. 279>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: C# generics: Тормоза. Что я не так делаю?
От: Денис Федин Россия  
Дата: 14.01.05 15:42
Оценка: +1 -1
Добрый день!
Уважаемый Павел Кузнецов, я знаю что вы профессионал, но высказывание типа: "вот это тормозит в 2 раза" очень некорректно, и очень далеко от истины, чтобы языку присваивать клеймо — "в 2 раза медленней!".
(Особенно было смешно про видео-плейер.)

Наберите в C# код:


for(int i = 0; i < 10000000; i++)
{
    string s = "123456789012345678901234567890";
    s += "1";
}


и наберите в C++ тот же самый код с использованием любого класса типа string.
Вы скорее всего предпочтете std::string.


for(int i = 0; i < 100000000; i++)
{
    std::string s = "123456789012345678901234567890";
    s += "1";        
}



Теперь скомпилируйте в release и сравните.

Я использовал:
MS VS C++ 7.0 и MS VS C#.

На моей машине Cel2500 такие показатели:
C++ — 46 сек.
C# — 18 сек.

Тогда я могу сделать однозначный вывод по поводу производительности?
Конечно, нет. Где-то быстрее, а где-то медленнее. Линейной и тем-более константной зависимости тут нет, и быть не может, Вы как профессионал должны это понимать.
Ясно одно, что в достаточно крупных приложениях очень часто используется дин. память, поэтому в словах Влада истина в том, что зачастую при миграции более менее крупного проекта с C++ на C# заметно повышение производительности на глаз.
Вообще суть то не в самом C#, а в dotNET, т.к. сборщик мусора включает в себя "грамотный" диспетчер дин. памяти, а отсюда и выигрыш в конечной производительности.
Страуструп очень умный человек и позволил переопределять оператор "new" как на локальном так и на глобальном уровне, что позволяет писать при надобности свои менеджеры дин. памяти, а не использовать встроенные в систему.
Но вопрос в том кто это делает?
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 12.01.05 15:57
Оценка: 21 (1)
Здравствуйте, VladD2, Вы писали:


VD> Гы. Думаю что он ради этого эти тесты и писал. Хотел убедиться, что пока у С++ кое-какие приемущества по оптимизации остались. Возможно даже малость в душе покоробило, что разница не ужасно большая.


Вообщето не совсем корректно сравнивать бэтту с отлаженным механизмом. А вот тенденции развития и совершенствования дженериков и оптимизации на лицо. А все остальное наживное. Москва не сразу строилась ... траля — ля (почему то нет смайлика пЕвца (одни пИвцы ))
и солнце б утром не вставало, когда бы не было меня
Re: C# generics: Тормоза. Что я не так делаю?
От: TK Лес кывт.рф
Дата: 10.01.05 10:46
Оценка: 18 (1)
Hello, "Павел Кузнецов"

> Я пробовал два варианта: с делегатом, как было в исходном сообщении, плюс, подозревая, что тормозят как раз делегаты, еще один вариант с прямым использованием члена CompareTo сортируемой структуры. По сравнению с boost::function делегаты отстают почти в 3 раза, прямой вызов CompareTo по сравнению с аналогичным кодом на C++ — почти в 2

>

У меня использование ngen делает из 250 единиц 220 (для CompareTo). Также, если сделать специализированную для IntWrapper версию с использованием указателей, то получается 190 ед (все тотже CompareTo).

Вот такая оптимизиция...
Posted via RSDN NNTP Server 1.9 alpha
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re: C# generics: Тормоза. Что я не так делаю?
От: alexeiz  
Дата: 11.01.05 07:32
Оценка: 18 (1)
"Павел Кузнецов" <5834@users.rsdn.ru> wrote in message
news:979456@news.rsdn.ru
>
> D:\Users\Pavel\test\C++\sort\Release>sort.exe AdbeRdr60_enu_full.exe
> Delegate...
> Sort duration: 1109
> Index: 3059506
> CompareTo...
> Sort duration: 547
> Index: 3059506
>

>
>
> D:\Users\Pavel\test\C#\sort\sort\bin\Release>sort.exe
> AdbeRdr60_enu_full.exe 
> Delegate...
> Sort duration: 3265.625
> Index: 3059506
> CompareTo...
> Sort duration: 1093.75
> Index: 3059506
>


KISS. В managed коде почти любая абстракция будет тебе стоить. Чем меньше абстракций, тем быстрее. Delegates и высокопроизводительный код — вещи вообще несовместимые. Даже виртуальный вызов быстрее, чем delegate. Imho лучше использовать IComparer, чем IComparable, который к тому-же можно объявить struct, чтобы вызовы Compare() были невиртуальными.

Измерения на 19MB файле (у меня память медленная по сравнению с процессором, поэтому результаты не в одинаковой пропорции с твоими):
// мой код
Compare...                                  
Sort duration: 1331.8088                      
Index: 997046                                 
               
// твой код                               
Delegate...                                   
Sort duration: 3134.2568                      
Index: 997046                                 
CompareTo...                                  
Sort duration: 2783.7808                      
Index: 997046


Код:
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;


class ComparableTest<T, Comp> : Test<T>
    where Comp
        : IComparer< T >
        , new()
{
     public ComparableTest(CreateElement create_element)
         : base( create_element )
     {
         comparer_ = new Comp();
     }

     protected override void Sort(T[] array)
     {
         Sort(array, 0, array.Length - 1);
     }

     private void Sort(T[] array, int left, int right)
     {
         int i = left;
         int j = right;
         T center = array[(left + right) / 2];

         while (i <= j)
         {
             while (comparer_.Compare( array[i], center ) < 0)
                 i++;

             while (comparer_.Compare( array[j], center ) > 0)
                 j--;

             if (i <= j)
             {
                 T x = array[i];
                 array[i] = array[j];
                 array[j] = x;
                 i++;
                 j--;
             }
         }

         if (left < j)
             Sort(array, left, j);

         if (right > i)
             Sort(array, i, right);
     }

     protected override int Search(T[] array, T value)
     {
         return BinarySearch(array, 0, array.Length, value);
     }

     private int BinarySearch(T[] array, int lo, int hi, T value)
     {
         while (lo <= hi)
         {
             int i = (lo + hi) / 2;
             int cmpResult = comparer_.Compare( array[i], value );

             if (cmpResult == 0)
                 return i;
             else if (cmpResult < 0)
                 lo = i + 1;
             else
                 hi = i - 1;
         }

         return ~lo;
     }

     // private
     Comp comparer_;
};


abstract class Test<T>
{
     public delegate T CreateElement(int value);

     protected Test(CreateElement create_element)
     {
         create_element_ = create_element;
     }

     public void Run(string fileName)
     {
         T[] array = CreateArray(fileName);
         T value = array[0];

         Console.WriteLine("Sort duration: " + Measure( delegate() { Sort(array); } ) );

         int index = Search(array, value);
         Console.WriteLine("Index: " + index);
     }

     protected delegate void Action();

     private double Measure(Action action)
     {
         DateTime start = DateTime.Now;
         action();
         DateTime end = DateTime.Now;
         return (end - start).TotalMilliseconds;
     }

     protected abstract void Sort(T[] array);

     protected abstract int Search(T[] array, T element);

     private T[] CreateArray(string fileName)
     {
         ArrayList array = new ArrayList();

         BinaryReader reader =
             new BinaryReader(File.Open(fileName, FileMode.Open));
         try
         {
             while (true)
             {
                 int value = reader.ReadInt32();
                 array.Add(create_element_(value));
             }
         }
         catch (EndOfStreamException)
         {
             // ignore
         }
         finally
         {
             reader.Close();
         }

         return (T[])array.ToArray(typeof(T));
     }

     private CreateElement create_element_;
};
 

struct IntComparer : IComparer< int >
{
    public int Compare( int a, int b )
    {
        return a - b;  // watch for integer overflow!
    }

    public bool Equals( int a, int b )
    {
        return a == b;
    }

    public int GetHashCode( int obj )
    {
        return obj;
    }
}


class Program
{
     static void Main(string[] args)
     {
         Console.WriteLine("Compare...");
         TestComparable(args[0]);
     }

     private static void TestComparable(string fileName)
     {
         Test<int>.CreateElement create_element_delegate =
             delegate(int value) { return value; };

         ComparableTest<int, IntComparer> test =
             new ComparableTest<int, IntComparer>(create_element_delegate);

         test.Run(fileName);
     }
}
Posted via RSDN NNTP Server 1.9
Re[6]: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 11.01.05 18:22
Оценка: 18 (1)
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Serginio1,


>> Да и по поводу считывания из файла лучше применить следующий метод

>> http://www.rsdn.ru/Forum/Message.aspx?mid=556030&amp;only=1
Автор: Yury_Malich
Дата: 02.03.04


ПК>Не совсем понял... В сообщении по ссылке четыре варианта, и все относятся к записи


Лучше исползовать не

while (true)
             {
                 int value = reader.ReadInt32();
                 array.Add(create_element_(value));
             }


А считывать в буффер byte[] , из которого через BlockCopy копировать в массив. Вопервых нетипизированный ArrayList это боксинг и унбоксинг,затраты памяти фрагментация памяти.

При копировании через буффер этих затрат нет. А заодно и сравнишь размеры твоего Интврайпера.
И соответственно GC срабатывать будет реже.
и солнце б утром не вставало, когда бы не было меня
Re: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.01.05 01:20
Оценка: 18 (1)
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>    public static IntWrapper operator -(IntWrapper l, IntWrapper r)
ПК>    {
ПК>        return new IntWrapper(l.value_ - r.value_);
ПК>    }

ПК>    public int CompareTo(IntWrapper r)
ПК>    {
ПК>        return this - r;
ПК>    }

ПК>         DelegateTest<IntWrapper>.CompareElement compare_element_delegate =
ПК>             delegate(IntWrapper x, IntWrapper y) { return x - y; };

ПК>

ПК>(*) Правда, оказалось, что то ли сортировка там не вполне сортирует, то ли поиск не вполне ищет, то ли

Прогаммисты хреновые...

ПК> и то, и другое, но это нам сейчас совершенно неважно


А, ну, да. У теста провекрки на вшивость не проходят, но это не важно... Разве это может повлиять на результат?

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

Я вот заменил в твоем тесте:
public int CompareTo(IntWrapper r)
{
        return this - r;
}

на:
public int CompareTo(IntWrapper r)
{
    return value_ - r.value_;
}

и получил удвоение производительности этого глюкалова. Вот только что я измеряю хоть убей сказать не могу.

Убедиться в моей правоте можно включив проверку переполения. Для Шарпа это делается в "Project\Properties+Build+Advanced".

В общем, нужно или делать проверку полноценной:
return _value > r._value ? 1 : _value == r._value ? 0 : -1;

или заменять делегаты и интерфейсы на содержащие метод Great и использовать в алгоритме сортировки его.

ПК>, поэтому прошу по этому поводу не пинать:




ПК> в любом случае, алгоритмы и в C#, и в C++ одни и те же, так что на время, что нас и интересует, это влиять не должно.


Ага. И главное полная гарантия, что переполнение обрабатывается одинаково и никаких проблем нет.

Как я уже сказал, замена пары байт кода в товем тесте сравнивает результаты (правда вывод этот основан на относительных результатах, так как С++-ного проекта у меня нет).

В общем, присылай С++-шный проект посмотрим...
... << RSDN@Home 1.1.4 beta 3 rev. 273>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: C# generics: Тормоза. Что я не так делаю?
От: TK Лес кывт.рф
Дата: 11.01.05 20:53
Оценка: 1 (1)
Hello, "Павел Кузнецов"
>
>> если это структура полями которой выступают примитивные типы, то по большому счету не так страшно, что туда запишется... приложение это не разрушит.
>
> Последнее понятно. Вопрос, будет ли запись корректной? В данном случае это, конечно, побоку, но тем не менее? Как-то не нравится "выжимать" такты за счет использования конструкций, корректность работы которых не гарантирована

Тут есть некоторая двойственность. Думаю, что сама запись корректна, но вот то, что операция sizeof является unsafe это смущает
Posted via RSDN NNTP Server 1.9
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[4]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 12.01.05 21:08
Оценка: 1 (1)
VladD2,

> ПК>Ок, сделаем проверку "полноценной".


> Кстати, тут большая вероятность того, что Шарп проиграет, так как если он не заинлайнит код (а он это может, так как код уже сложноватый для того чтобы джит с ним возился).

>
> Так что если тебе охота поглядеть именно потенциал, то лучше бы конечно попробовать и вариант с кастом-интерфейсом.

Мне охота посмотреть возможно ли на Шарпе эффективно писать generic код, работающий с value-типами, и если возможно, насколько легко это дается. Пока, кажется, что нелегко.

> ты меряешь на грани разумного. Здесь любая мелочь может дать разницу в два-три раза.


Для C++ это не так: обычно abstraction penalty колеблется в пределах десятка процентов, если вообще присутствует.

> Это уже не архитектурная разница, а борьба команд оптимизаторов.


Для меня больше вопрос не в абсолютных цифрах C# vs. C++, а в относительных: насколько падает быстродействие C# для арифметики и около того при использовании мало-мальской абстракции над встроенными типами и попытках повторного использования кода. Пока результаты не радуют.

> Я изучение твоего кода сразу начал с вырезания лишнего жира. Если охота могу прислать солющен. Я там еще пару вариантов компареров забубенил.


Да, конечно, было бы интересно.

> Ты С++-ный проект выложил бы в файлы. Да и Шарповый тоже.


Я выложил полные листинги всех файлов, которые использовал.

> ПК> Но и тесты, в общем-то несколько изменились... Также можно заметить, что теперь в C# и int тормозит почти так же, как IntWrapper. Ниже привожу новые результаты и код, по просьбам телезрителей включая C++

>
> Ты кстати, мерил бы вот этим: Точное измерение производительности в дотнете
Автор: VladD2
Дата: 23.04.03
.


Для этих тестов мне вполне достаточно используемой точности.

> Вот результаты твоего подтюнегого кода (в секундах, Load это скорость загрузки, корректность проверяется ассертами): <...> С++-ного проекта небыло, так чта...


Гм... В том же сообщении полный код на C++

> А вот сам проект.


Ok, посмотрю.

> Кстати, ради хохмы скомпилируй тест на компиляторах отличных от VC. Думаю некоторые могут значительно слить. Особенно разные Борланды.


С удовольствием, если руки дойдут.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[9]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 11.01.05 20:23
Оценка: +1
TK,

> если это структура полями которой выступают примитивные типы, то по большому счету не так страшно, что туда запишется... приложение это не разрушит.


Последнее понятно. Вопрос, будет ли запись корректной? В данном случае это, конечно, побоку, но тем не менее? Как-то не нравится "выжимать" такты за счет использования конструкций, корректность работы которых не гарантирована
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[2]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 12.01.05 06:22
Оценка: :)
VladD2,

> ПК>(*) Правда, оказалось, что то ли сортировка там не вполне сортирует, то ли поиск не вполне ищет, то ли

>
> Прогаммисты хреновые...

Возможно. Код взят из твоего сообщения
Автор: VladD2
Дата: 25.10.04
. Функция сравнения осталась такой же, какой она была у тебя. Кста, данный казус лишний раз демонстрирует, что ни язык, ни среда исполнения не могут в достаточной степени ловить проявления небрежности

Мне было более-менее без разницы, что именно делает тест, абы это что-то было одним и тем же, как для C#, так и для C++. Это условие, имхо, выполнялось.

Твое наблюдение о переполнении верно. Интересно только, почему для IntWrapper и int была такая разница в быстродействии при том, что по идее работа делалась та же самая...

> В общем, нужно или делать проверку полноценной:

>
> return _value > r._value ? 1 : _value == r._value ? 0 : -1;
>


Ок, сделаем проверку "полноценной".

> ПК> в любом случае, алгоритмы и в C#, и в C++ одни и те же, так что на время, что нас и интересует, это влиять не должно.


> Ага. И главное полная гарантия, что переполнение обрабатывается одинаково и никаких проблем нет.


Ну, судя по тому, что индекс в результате находился один и тот же, с хорошей долей уверенности можно предположить, что и остальные итоги вычислений одни и те же. Как бы то ни было, теперь есть новые тесты, вроде, без арифметического переполнения.



Единственное, просто поправить не получилось: увлекся обобщением и упрощением, пытаясь и в C# уменьшить дублирование кода Разница немного упала, 3-кратного преимущества C++, вроде, не наблюдается, теперь разрыв "всего лишь" в 2 раза. Но и тесты, в общем-то несколько изменились... Также можно заметить, что теперь в C# и int тормозит почти так же, как IntWrapper. Ниже привожу новые результаты и код, по просьбам телезрителей включая C++

D:\Users\Pavel\test\C#\sort\sort\bin\Release>sort.exe AdbeRdr60_enu_full.exe
Delegate...       Sort: 2218     (index: 2096905)
                         2296     (index: 2096905)
                         2296     (index: 2096905)
                         2437     (index: 2096905)
                         2296     (index: 2096905)
Comparer...       Sort: 1390     (index: 2096905)
                         1390     (index: 2096905)
                         1375     (index: 2096905)
                         1375     (index: 2096905)
                         1375     (index: 2096905)
Delegate (int)... Sort: 1968     (index: 2096905)
                         1968     (index: 2096905)
                         1953     (index: 2096905)
                         1968     (index: 2096905)
                         1984     (index: 2096905)
Comparer (int)... Sort: 1203     (index: 2096905)
                         1187     (index: 2096905)
                         1203     (index: 2096905)
                         1203     (index: 2096905)
                         1203     (index: 2096905)


D:\Users\Pavel\test\C++\sort\Release>sort.exe AdbeRdr60_enu_full.exe
Delegate...       Sort: 1157    (index: 2096905)
                         1188    (index: 2096905)
                         1141    (index: 2096905)
                         1125    (index: 2096905)
                         1125    (index: 2096905)
Comparer...       Sort: 657     (index: 2096905)
                         656     (index: 2096905)
                         656     (index: 2096905)
                         656     (index: 2096905)
                         657     (index: 2096905)
Delegate (int)... Sort: 1234    (index: 2096905)
                         1234    (index: 2096905)
                         1219    (index: 2096905)
                         1203    (index: 2096905)
                         1219    (index: 2096905)
Comparer (int)... Sort: 671     (index: 2096905)
                         657     (index: 2096905)
                         672     (index: 2096905)
                         656     (index: 2096905)
                         656     (index: 2096905)




using System;

struct IntWrapper : IComparable<IntWrapper>
{
     public IntWrapper(int value)
     {
         value_ = value;
     }

     public static bool operator <(IntWrapper l, IntWrapper r)
     {
         return l.value_ < r.value_;
     }

     public static bool operator >(IntWrapper l, IntWrapper r)
     {
         return l.value_ > r.value_;
     }

     public static bool operator ==(IntWrapper l, IntWrapper r)
     {
         return l.value_ == r.value_;
     }

     public int CompareTo(IntWrapper r)
     {
         return this > r ? 1 : this == r ? 0 : -1;
     }

     // Following are here just to keep the compiler silent.
     public static bool operator !=(IntWrapper l, IntWrapper r) { return l.value_ != r.value_; }
     public bool Equals(IntWrapper r) { return this == r; }
     public override bool Equals(Object r) { return this == (IntWrapper)r; }
     public override int GetHashCode() { return value_; }

     // private
     int value_;
};


using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

interface IFactory<T>
{
     T Create(int value);
};

class Test<T, Comparer, Factory>
     where Comparer : IComparer<T>, new()
     where Factory : IFactory<T>, new()
{
     public Test(string name)
     {
         name_ = name;
         comparer_ = new Comparer();
     }

     public void Run(string fileName)
     {
         string prefix = name_ + "Sort: ";

         for (int i = 0; i < 5; ++i)
         {
             T[] array = CreateArray(fileName);
             T value = array[0];

             Console.Write(prefix + (int)Measure(delegate() { Sort(array); }));

             int index = Search(array, value);
             Console.WriteLine("\t (index: " + index + ")");

             if (i == 0)
                 prefix = new string(' ', prefix.Length);
         }
     }

     // private

     void Sort(T[] array)
     {
         Sort(array, 0, array.Length - 1);
     }

     void Sort(T[] array, int left, int right)
     {
         int i = left;
         int j = right;
         T center = array[(left + right) / 2];

         while (i <= j)
         {
             while (comparer_.Compare(array[i], center) < 0)
                 i++;

             while (comparer_.Compare(array[j], center) > 0)
                 j--;

             if (i <= j)
             {
                 T x = array[i];
                 array[i] = array[j];
                 array[j] = x;
                 i++;
                 j--;
             }
         }

         if (left < j)
             Sort(array, left, j);

         if (right > i)
             Sort(array, i, right);
     }

     int Search(T[] array, T value)
     {
         return BinarySearch(array, 0, array.Length, value);
     }

     int BinarySearch(T[] array, int lo, int hi, T value)
     {
         while (lo <= hi)
         {
             int i = (lo + hi) / 2;
             int cmpResult = comparer_.Compare(array[i], value);

             if (cmpResult == 0)
                 return i;
             else if (cmpResult < 0)
                 lo = i + 1;
             else
                 hi = i - 1;
         }

         return ~lo;
     }

     T[] CreateArray(string fileName)
     {
         Factory factory = new Factory();

         using (BinaryReader reader =
             new BinaryReader(File.Open(fileName, FileMode.Open)))
         {
             long len = reader.BaseStream.Length / 4;
             T[] array = new T[len];

             for (int i = 0; i < len; i++)
                 array[i] = factory.Create(reader.ReadInt32());

             return array;
         }
     }

     delegate void Action();
     double Measure(Action action)
     {
         DateTime start = DateTime.Now;
         action();
         DateTime end = DateTime.Now;
         return (end - start).TotalMilliseconds;
     }

     string name_;
     Comparer comparer_;
};


using System;
using System.Collections.Generic;
using System.Text;

struct DirectComparer<T> : IComparer<T>
     where T : IComparable<T>
{
     public int Compare(T l, T r) { return l.CompareTo(r); }
     public bool Equals(T l, T r) { return l.CompareTo(r) == 0; }
     public int GetHashCode(T obj){ return obj.GetHashCode(); }
}

struct DelegateComparer<T> : IComparer<T>
     where T : IComparable<T>
{
     public int Compare(T l, T r) { return compare_element_(l, r); }
     public bool Equals(T l, T r) { return compare_element_(l, r) == 0; }
     public int GetHashCode(T obj){ return obj.GetHashCode(); }

     // private
     delegate int CompareElement(T x, T y);
     static CompareElement compare_element_ = delegate(T x, T y) { return x.CompareTo(y); };
}

struct IntWrapperFactory : IFactory<IntWrapper>
{
     public IntWrapper Create(int value) { return new IntWrapper(value); }
}

struct IntFactory : IFactory<int>
{
     public int Create(int value) { return value; }
}

class Program
{
     static void Main(string[] args)
     {
         RunTest<IntWrapper, DelegateComparer<IntWrapper>, IntWrapperFactory>(
             args[0], "Delegate...       ");

         // RunTest<IntWrapper, ???, IntWrapperFactory>(args[0], "CompareTo...");

         RunTest<IntWrapper, DirectComparer<IntWrapper>, IntWrapperFactory>(
             args[0], "Comparer...       ");

         RunTest<int, DelegateComparer<int>, IntFactory>(
             args[0], "Delegate (int)... ");

         // RunTest<int, ???, IntFactory>(args[0], "CompareTo (int)...");

         RunTest<int, DirectComparer<int>, IntFactory>(
             args[0], "Comparer (int)... ");
     }

     private static void RunTest<T, Comparer, Factory>(string fname, string test_name)
         where T : IComparable<T>
         where Comparer : IComparer<T>, new()
         where Factory : IFactory<T>, new()
     {
         Test<T, Comparer, Factory> test = new Test<T, Comparer, Factory>(test_name);
         test.Run(fname);
     }
}




IntWrapper.h
#pragma once

struct IntWrapper
{
public:
     IntWrapper(int value)
     : value_ (value)
     {
     }

     friend bool operator <(IntWrapper l, IntWrapper r)
     {
         return l.value_ < r.value_;
     }

     friend bool operator >(IntWrapper l, IntWrapper r)
     {
         return l.value_ > r.value_;
     }

     friend bool operator ==(IntWrapper l, IntWrapper r)
     {
         return l.value_ == r.value_;
     }

private:
     int value_;
};

template< class T >
int CompareTo( T l, T r )
{
     return l > r ? 1 : l == r ? 0 : -1;
}

Sort.h
#pragma once

#include <vector>

template< class T, template<class> class Comparer >
void Sort(std::vector<T>& v, std::size_t left, std::size_t right)
{
     std::size_t i = left;
     std::size_t j = right;
     T center = v[(left + right) / 2];

     while (i <= j)
     {
         while (Comparer<T>::compare(v[i], center) < 0)
             i++;

         while (Comparer<T>::compare(v[j], center) > 0)
             j--;

         if (i <= j)
         {
             T x = v[i];
             v[i] = v[j];
             v[j] = x;
             i++;
             j--;
         }
     }

     if (left < j)
         Sort<T, Comparer>(v, left, j);

     if (right > i)
         Sort<T, Comparer>(v, i, right);
}

template< class T, template<class> class Comparer >
void Sort(std::vector<T>& v)
{
     if (!v.empty())
         Sort<T, Comparer>(v, 0, v.size() - 1);
}

Search.h
#pragma once

#include <vector>

template< class T, template<class> class Comparer >
std::size_t
BinarySearch(std::vector<T> const& v, std::size_t lo, std::size_t hi, T value)
{
     while (lo <= hi)
     {
         std::size_t i = (lo + hi) / 2;
         int cmpResult = Comparer<T>::compare(v[i], value);

         if (cmpResult == 0)
             return i;
         else if (cmpResult < 0)
             lo = i + 1;
         else
             hi = i - 1;
     }

     return ~lo;
}

template< class T, template<class> class Comparer >
std::size_t
Search(std::vector<T> const& v, T value)
{
     return BinarySearch<T, Comparer>(v, 0, v.size(), value);
}

Test.h
#pragma once

#include <string>
#include <vector>
#include <fstream>

#include "Timer.h"
#include "Sort.h"
#include "Search.h"

template< class T >
void CreateArray(std::vector<T>& v, std::string const& fname)
{
     v.clear();

     std::filebuf reader;
     if (reader.open(fname.c_str(), std::ios::in | std::ios::binary))
     {
         int value;
         while (reader.sgetn(reinterpret_cast<char*>(&value), 4) == 4)
         v.push_back(T(value));
     }
}

template< class T, template<class> class Comparer >
void Test(std::string const& fname, std::string const& test_name)
{
     std::string prefix = test_name + "Sort: ";

     for (int i = 0; i < 5; ++i)
     {
         std::vector<T> v;
         CreateArray(v, fname);

         if ( !v.empty() )
         {
             T value = v[0];

             std::cout << prefix;

             Timer timer;
             Sort<T, Comparer>(v);
             std::cout << timer.time();

             std::cout
                 << "\t(index: "
                 << (unsigned)Search<T, Comparer>(v, value)
                 << ')'
                 << std::endl;

             if (i == 0)
                 prefix.assign(prefix.size(), ' ');
         }
     }
}

Timer.h
#pragma once

#include <ctime>

class Timer
{
public:
     Timer() { reset(); }

     void reset() { start_ = std::clock(); }

     std::clock_t time() const { return (std::clock() - start_) / ( CLOCKS_PER_SEC / 1000 ); }

private:
     std::clock_t start_;
};

Program.cpp
#include <boost/function/function2.hpp>

#include "Test.h"
#include "IntWrapper.h"

template< class T >
struct DirectComparer
{
     static int compare( T l, T r ) { return CompareTo(l, r); }
};

template< class T >
struct DelegateComparer
{
     static int compare(T l, T r) { return delegate(l, r); }
     static boost::function2<int, T, T> delegate;
};

template< class T >
struct Functor
{
     int operator()(T l, T r) const { return CompareTo(l, r); }
};

template< class T >
boost::function2<int, T, T>
DelegateComparer<T>::delegate = Functor<T>();

int main(int argc, char** argv)
{
     if ( argc <= 1 )
     {
         std::cout << "usage: <program> <filename>" << std::endl;
     }
     else
     {
        Test<IntWrapper, DelegateComparer>(argv[1], "Delegate...       ");
        Test<IntWrapper, DirectComparer>(argv[1],   "Comparer...       ");
        Test<int, DelegateComparer>(argv[1],        "Delegate (int)... ");
        Test<int, DirectComparer>(argv[1],          "Comparer (int)... ");
     }
}
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 13.01.05 02:13
Оценка: :)
Serginio1,

> ПК>struct DirectComparer<T> : IComparer<T>

> ПК> where T : IComparable<T>
> ПК>{
> ПК> public int Compare(T l, T r) { return l.CompareTo(r); }
>
> В данном коде никакого инлайна не будет (веренее теле инлайна будет вызов l.CompareTo(r) <...>

Ох, точно, "опять об Гоголя" Ща попробуем по-другому

> ПК> static CompareElement compare_element_ = delegate(T x, T y) { return x.CompareTo(y); };


> Вот любители перегрузок. Вызов виртуальных (не инлайновых) компараторов не смущает. Или долго сделать специализацию компараторов конкретного типа, исключая виртуальность и используя инлайн????


Кажется, я начинаю понимать. Чтобы писать относительно эффективный обобщенный код на C#, его в определенных местах нужно разбавлять копипастой

> Перегрузку операторов тебе не лень писать. Зачем такие сложности


Что значит "сложности"? Я хочу повторно использовать один раз написанные функции, а не плодить вместо них "ускорители" В голове в качестве "жизненного" примера я держу шаблоны классов, встречавшихся в реальных проектах: "маленькие" вектора:Vector2<T>, Vector3<T>, Vector4<T> (можно еще обобщить, сделав Vector<dimension,T>); соответствующие им "маленькие" матрицы: Matrix2x2<T> и т.п. Как только начинаются сколько-нибудь существенные вычисления, там очень быстро устаешь размножать копипастой всевозможные операции с векторами и матрицами; да еще и ошибки надоедает вылавливать. Естественно, хочется все это дело один раз написать, и везде использовать. То, что видно в тестовом коде только обозначает характерные моменты.

В самом деле, цель данного теста не написать код с максимальной производительностью (тогда бы можно было начать делать ассемблерные вставки и т.п.), а выяснить, в какой степени приемы, ориентированные на повышение уровня абстракции и повторного использования, влияют на производительность.

Ладно, на что не пойдешь в целях эксперимента... Ща, для сравнения, наколбасим "ручных" специализаций.

P.S. Кста, в плюсовом коде, что выложен в прошлом сообщении, потенциальная ошибка: там напрасно сделана замена int -> std::size_t в Search.h. Похоже, если это дело дальше развивать, нужно взять какую-нибудь реализацию сортировки и т.п., устойчивой к подобным изменениям...
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 10.01.05 07:27
Оценка:
Наконец, поставил бету новой студии. Вдохновленный рассказами о чудесах оптимизатора C#, решил поиграться с C# generics... В общем, почему-то на моих тестах тормозит в 2-3 раза по сравнению с аналогичным кодом на C++. Я подозреваю собственные кривые руки, т.к. с C# знаком весьма посредственно. Помогите, пожалуйста, разобраться, что я не так делаю.

Мне было интересно сравнить использование аналогичных техник в C# и C++ для сортировки структуры—обертки над int. Чтобы не мучиться с алгоритмом, взял из первого попавшегося сообщения
Автор: VladD2
Дата: 25.10.04
(*)

Я пробовал два варианта: с делегатом, как было в исходном сообщении, плюс, подозревая, что тормозят как раз делегаты, еще один вариант с прямым использованием члена CompareTo сортируемой структуры. По сравнению с boost::function делегаты отстают почти в 3 раза, прямой вызов CompareTo по сравнению с аналогичным кодом на C++ — почти в 2



D:\Users\Pavel\test\C++\sort\Release>sort.exe AdbeRdr60_enu_full.exe
Delegate...
Sort duration: 1109
Index: 3059506
CompareTo...
Sort duration: 547
Index: 3059506


D:\Users\Pavel\test\C#\sort\sort\bin\Release>sort.exe AdbeRdr60_enu_full.exe
Delegate...
Sort duration: 3265.625
Index: 3059506
CompareTo...
Sort duration: 1093.75
Index: 3059506




Обертка над int:
struct IntWrapper : IComparable<IntWrapper>
{
    public IntWrapper(int value)
    {
        value_ = value;
    }

    public static implicit operator int(IntWrapper w)
    {
        return w.value_;
    }

    public static IntWrapper operator -(IntWrapper l, IntWrapper r)
    {
        return new IntWrapper(l.value_ - r.value_);
    }

    public int CompareTo(IntWrapper r)
    {
        return this - r;
    }

    public bool Equals(IntWrapper r)
    {
        return CompareTo(r) == 0;
    }

    private int value_;
};


Тесты:
class ComparableTest<T> : Test<T> where T : IComparable<T>
{
     public ComparableTest(CreateElement create_element)
     : base( create_element )
     {
     }

     protected override void Sort(T[] array)
     {
         Sort(array, 0, array.Length - 1);
     }

     private static void Sort(T[] array, int left, int right)
     {
         int i = left;
         int j = right;
         T center = array[(left + right) / 2];

         while (i <= j)
         {
             while (array[i].CompareTo(center) < 0)
                 i++;

             while (array[j].CompareTo(center) > 0)
                 j--;

             if (i <= j)
             {
                 T x = array[i];
                 array[i] = array[j];
                 array[j] = x;
                 i++;
                 j--;
             }
         }

         if (left < j)
             Sort(array, left, j);

         if (right > i)
             Sort(array, i, right);
     }

     protected override int Search(T[] array, T value)
     {
         return BinarySearch(array, 0, array.Length, value);
     }

     private static int BinarySearch(T[] array, int lo, int hi, T value)
     {
         while (lo <= hi)
         {
             int i = (lo + hi) / 2;
             int cmpResult = array[i].CompareTo(value);

             if (cmpResult == 0)
                 return i;
             else if (cmpResult < 0)
                 lo = i + 1;
             else
                 hi = i - 1;
         }

         return ~lo;
     }
};


class DelegateTest<T> : Test<T>
{
     public delegate int CompareElement(T x, T y);

     public DelegateTest(CreateElement create_element, CompareElement compare_element)
     : base( create_element )
     {
         compare_element_ = compare_element;
     }

     protected override void Sort(T[] array)
     {
         Sort(array, 0, array.Length - 1, compare_element_);
     }

     private static void Sort(T[] array, int left, int right, CompareElement compare)
     {
         int i = left;
         int j = right;
         T center = array[(left + right) / 2];

         while (i <= j)
         {
             while (compare(array[i], center) < 0)
                 i++;

             while (compare(array[j], center) > 0)
                 j--;

             if (i <= j)
             {
                 T x = array[i];
                 array[i] = array[j];
                 array[j] = x;
                 i++;
                 j--;
             }
         }

         if (left < j)
             Sort(array, left, j, compare);

         if (right > i)
             Sort(array, i, right, compare);
     }

     protected override int Search(T[] array, T value)
     {
         return BinarySearch(array, 0, array.Length, value, compare_element_);
     }

     private static int BinarySearch(T[] array, int lo, int hi, T value, CompareElement compare)
     {
         while (lo <= hi)
         {
             int i = (lo + hi) / 2;
             int cmpResult = compare(array[i], value);

             if (cmpResult == 0)
                 return i;
             else if (cmpResult < 0)
                 lo = i + 1;
             else
                 hi = i - 1;
         }

         return ~lo;
     }

     CompareElement compare_element_;
};


Общая база для тестов:
abstract class Test<T>
{
     public delegate T CreateElement(int value);

     protected Test(CreateElement create_element)
     {
         create_element_ = create_element;
     }

     public void Run(string fileName)
     {
         T[] array = CreateArray(fileName);
         T value = array[0];

         Console.WriteLine("Sort duration: " + Measure( delegate() { Sort(array); } ) );

         int index = Search(array, value);
         Console.WriteLine("Index: " + index);
     }

     protected delegate void Action();

     private double Measure(Action action)
     {
         DateTime start = DateTime.Now;
         action();
         DateTime end = DateTime.Now;
         return (end - start).TotalMilliseconds;
     }

     protected abstract void Sort(T[] array);

     protected abstract int Search(T[] array, T element);

     private T[] CreateArray(string fileName)
     {
         ArrayList array = new ArrayList();

         BinaryReader reader =
             new BinaryReader(File.Open(fileName, FileMode.Open));
         try
         {
             while (true)
             {
                 int value = reader.ReadInt32();
                 array.Add(create_element_(value));
             }
         }
         catch (EndOfStreamException)
         {
             // ignore
         }
         finally
         {
             reader.Close();
         }

         return (T[])array.ToArray(typeof(T));
     }

     private CreateElement create_element_;
};


Запуск тестов:
class Program
{
     static void Main(string[] args)
     {
         Console.WriteLine("Delegate...");
         TestDelegate(args[0]);

         Console.WriteLine("CompareTo...");
         TestComparable(args[0]);
     }

     private static void TestDelegate(string fileName)
     {
         Test<IntWrapper>.CreateElement create_element_delegate =
             delegate(int value) { return new IntWrapper(value); };

         DelegateTest<IntWrapper>.CompareElement compare_element_delegate =
             delegate(IntWrapper x, IntWrapper y) { return x - y; };

         DelegateTest<IntWrapper> test =
             new DelegateTest<IntWrapper>(
                 create_element_delegate,
                 compare_element_delegate
                 );

         test.Run(fileName);
     }

     private static void TestComparable(string fileName)
     {
         Test<IntWrapper>.CreateElement create_element_delegate =
             delegate(int value) { return new IntWrapper(value); };

         ComparableTest<IntWrapper> test =
             new ComparableTest<IntWrapper>(create_element_delegate);

         test.Run(fileName);
     }
}




(*) Правда, оказалось, что то ли сортировка там не вполне сортирует, то ли поиск не вполне ищет, то ли и то, и другое, но это нам сейчас совершенно неважно, поэтому прошу по этому поводу не пинать: в любом случае, алгоритмы и в C#, и в C++ одни и те же, так что на время, что нас и интересует, это влиять не должно.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[2]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 11.01.05 12:54
Оценка:
alexeiz,

> KISS.


ROFL

> В managed коде почти любая абстракция будет тебе стоить. Чем меньше абстракций, тем быстрее.


Да уж... Особенно "порадовало", что из-за ограничений generics приходится дублировать код даже для написания самих тестов

Собственно, именно плату за повышение уровня абстракции я и хотел себе представить. Пожалуй, можно будет адаптировать на C# Abstraction penalty benchmark Степанова, если захочется увидеть более полную картину.

> Imho лучше использовать IComparer, чем IComparable, который к тому-же можно объявить struct, чтобы вызовы Compare() были невиртуальными.


Спасибо за вариант. Что интересно, твой вариант является самым быстрым для int, а для IntWrapper, по крайней мере, на моей машине, самым быстрым остается CompareTo. У меня серьезное ощущение, что дело не только в IComparer вместо IComparable, а еще и в int вместо IntWrapper:
D:\Users\Pavel\test\C#\sort\sort\bin\Release>sort.exe AdbeRdr60_enu_full.exe
Delegate...
Sort duration: 2953.125
Index: 3059506
CompareTo...
Sort duration: 1171.875
Index: 3059506
Comparator...
Sort duration: 1250
Index: 3059506
Delegate (int)...
Sort duration: 1093.75
Index: 3059506
CompareTo (int)...
Sort duration: 937.5
Index: 2096905
Comparator (int)...
Sort duration: 578.125
Index: 3059506


struct IntWrapperComparer : IComparer<IntWrapper>
{
     public int Compare(IntWrapper a, IntWrapper b)
     {
         return a - b;  // watch for integer overflow!
     }

     public bool Equals(IntWrapper a, IntWrapper b)
     {
         return a == b;
     }

     public int GetHashCode(IntWrapper obj)
     {
         return obj;
     }
};


class Program
{
     static void Main(string[] args)
     {
         Console.WriteLine("Delegate...");
         TestDelegate(args[0]);

         Console.WriteLine("CompareTo...");
         TestComparable(args[0]);

         Console.WriteLine("Comparator...");
         TestComparator(args[0]);

         Console.WriteLine("Delegate (int)...");
         TestIntDelegate(args[0]);

         Console.WriteLine("CompareTo (int)...");
         TestIntComparable(args[0]);

         Console.WriteLine("Comparator (int)...");
         TestIntComparator(args[0]);
     }

     private static void TestDelegate(string fileName)
     {
         Test<IntWrapper>.CreateElement create_element_delegate =
             delegate(int value) { return new IntWrapper(value); };

         DelegateTest<IntWrapper>.CompareElement compare_element_delegate =
             delegate(IntWrapper x, IntWrapper y) { return x - y; };

         DelegateTest<IntWrapper> test =
             new DelegateTest<IntWrapper>(
                 create_element_delegate,
                 compare_element_delegate
                 );

         test.Run(fileName);
     }

     private static void TestComparable(string fileName)
     {
         Test<IntWrapper>.CreateElement create_element_delegate =
             delegate(int value) { return new IntWrapper(value); };

         ComparableTest<IntWrapper> test =
             new ComparableTest<IntWrapper>(create_element_delegate);

         test.Run(fileName);
     }

     private static void TestComparator(string fileName)
     {
         Test<IntWrapper>.CreateElement create_element_delegate =
             delegate(int value) { return new IntWrapper(value); };

         ComparatorTest<IntWrapper, IntWrapperComparer> test =
             new ComparatorTest<IntWrapper, IntWrapperComparer>(create_element_delegate);

         test.Run(fileName);
     }

     private static void TestIntDelegate(string fileName)
     {
         Test<int>.CreateElement create_element_delegate =
             delegate(int value) { return value; };

         DelegateTest<int>.CompareElement compare_element_delegate =
             delegate(int x, int y) { return x - y; };

         DelegateTest<int> test =
             new DelegateTest<int>(
                 create_element_delegate,
                 compare_element_delegate
                 );

         test.Run(fileName);
     }

     private static void TestIntComparable(string fileName)
     {
         Test<int>.CreateElement create_element_delegate =
             delegate(int value) { return value; };

         ComparableTest<int> test =
             new ComparableTest<int>(create_element_delegate);

         test.Run(fileName);
     }

     private static void TestIntComparator(string fileName)
     {
         Test<int>.CreateElement create_element_delegate =
              delegate(int value) { return value; };

         ComparatorTest<int, IntComparer> test =
             new ComparatorTest<int, IntComparer>(create_element_delegate);

         test.Run(fileName);
     }
}
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 11.01.05 14:56
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Мне было интересно сравнить использование аналогичных техник в C# и C++ для сортировки структуры—обертки над int. Чтобы не мучиться с алгоритмом, взял из первого попавшегося сообщения
Автор: VladD2
Дата: 25.10.04
(*)


Лучше конечно
http://www.rsdn.ru/article/devtools/newinwhitbey2.xml#EFA
Автор(ы): Владислав Чистяков (VladD2)
Дата: 27.07.2004
Статья является продолжением цикла статей, опубликованных в номере 6 за 2003 год. В ней рассказывается о нововведениях, появившихся в новой версии VS 2005 (Whidbey) и .NET Framework. Упор делается в первую очередь на нововведения, связанные с программированием на C#.
и солнце б утром не вставало, когда бы не было меня
Re[2]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 11.01.05 17:05
Оценка:
Serginio1,

> ПК> Мне было интересно сравнить использование аналогичных техник в C# и C++ для сортировки структуры—обертки над int. Чтобы не мучиться с алгоритмом, взял из первого попавшегося сообщения
Автор: VladD2
Дата: 25.10.04
(*)


> Лучше конечно

> http://www.rsdn.ru/article/devtools/newinwhitbey2.xml#EFA
Автор(ы): Владислав Чистяков (VladD2)
Дата: 27.07.2004
Статья является продолжением цикла статей, опубликованных в номере 6 за 2003 год. В ней рассказывается о нововведениях, появившихся в новой версии VS 2005 (Whidbey) и .NET Framework. Упор делается в первую очередь на нововведения, связанные с программированием на C#.


Если речь идет об улучшении работы кода C# за счет перехода на IComparer<>, то результаты получаются весьма интересными. Аналогичный вариант привел
Автор: alexeiz
Дата: 11.01.05
alexeiz. Он, действительно, быстрее для int. Но для IntWrapper еще медленнее
Автор: Павел Кузнецов
Дата: 11.01.05
, чем вариант с вызовом CompareTo.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 11.01.05 17:34
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:


>> Лучше конечно

>> http://www.rsdn.ru/article/devtools/newinwhitbey2.xml#EFA
Автор(ы): Владислав Чистяков (VladD2)
Дата: 27.07.2004
Статья является продолжением цикла статей, опубликованных в номере 6 за 2003 год. В ней рассказывается о нововведениях, появившихся в новой версии VS 2005 (Whidbey) и .NET Framework. Упор делается в первую очередь на нововведения, связанные с программированием на C#.


ПК>Если речь идет об улучшении работы кода C# за счет перехода на IComparer<>, то результаты получаются весьма интересными. Аналогичный вариант привел
Автор: alexeiz
Дата: 11.01.05
alexeiz. Он, действительно, быстрее для int. Но для IntWrapper еще медленнее
Автор: Павел Кузнецов
Дата: 11.01.05
, чем вариант с вызовом CompareTo.

Достаточно странное поведение. Но здесь может срабатывать GC поэтому желательно делать по несколько тестов.
Разница бывает очень ощютима. А разницы межды int и IntWrapper вроде нет, или инлайнит не совсем правильно.
В любом случае нужно еще проверить. Как бы там не было это только бэтта. Но ситуация с дженериками постоянно исправляется к лучшему.
и солнце б утром не вставало, когда бы не было меня
Re[4]: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 11.01.05 17:38
Оценка:
Здравствуйте, Serginio1, Вы писали:

Да и по поводу считывания из файла лучше применить следующий метод
http://www.rsdn.ru/Forum/Message.aspx?mid=556030&amp;only=1
Автор: Yury_Malich
Дата: 02.03.04


Так намного кошернее
и солнце б утром не вставало, когда бы не было меня
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 11.01.05 18:09
Оценка:
Serginio1,

> Да и по поводу считывания из файла лучше применить следующий метод

> http://www.rsdn.ru/Forum/Message.aspx?mid=556030&amp;only=1
Автор: Yury_Malich
Дата: 02.03.04


Не совсем понял... В сообщении по ссылке четыре варианта, и все относятся к записи
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 11.01.05 18:16
Оценка:
Serginio1,

> ПК> для IntWrapper еще медленнее
Автор: Павел Кузнецов
Дата: 11.01.05
, чем вариант с вызовом CompareTo.


> Достаточно странное поведение. Но здесь может срабатывать GC поэтому желательно делать по несколько тестов.


Несколько запусков? Так и делалось. Результаты варьируются в достаточно небольших пределах. Во всяком случае разброс не влияет на "рейтинг" результатов относительно друг друга.

> А разницы межды int и IntWrapper вроде нет, или инлайнит не совсем правильно.


На практике разница, как можно заметить, почему-то есть. В этом топике те же проблемы: http://www.rsdn.ru/forum/Message.aspx?mid=978528#978528
Автор:
Дата: 09.01.05


> В любом случае нужно еще проверить. Как бы там не было это только бэтта. Но ситуация с дженериками постоянно исправляется к лучшему.


Честно говоря, больше всего напрягла необходимость дублировать код для каждого из тестов из-за отсутствия С++-подобных шаблонов без ограничений, присущих generics. Думаю, может, переписать тест на C++/CLI? Заодно увидим, может, там оптимизация удачнее
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[7]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 11.01.05 19:02
Оценка:
Serginio1,

> считывать в буффер byte[] , из которого через BlockCopy копировать в массив.


А можно ли через BlockCopy копировать побайтно в массив IntWrapper[]? Что-то я не нашел гарантий работоспособности этого способа в стандарте C#. По первому впечатлению попахивает использованием для аналогичных целей memcpy в C++ со всеми вытекающими.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[8]: C# generics: Тормоза. Что я не так делаю?
От: Mika Soukhov Stock#
Дата: 11.01.05 19:53
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Serginio1,


>> считывать в буффер byte[] , из которого через BlockCopy копировать в массив.


ПК>А можно ли через BlockCopy копировать побайтно в массив IntWrapper[]? Что-то я не нашел гарантий работоспособности этого способа в стандарте C#. По первому впечатлению попахивает использованием для аналогичных целей memcpy в C++ со всеми вытекающими.


Можно попробовать через Marshal.Copy. Например:
// для примера данные инициализируются только на стеке
IntWrapper* array = stackalloc IntWrapper[arraySize];
Marshal.Copy(streamAsByteArray/*данные из потока*/, 0, new IntPtr(array), sizeof(int));


А вообще пора уже избавляться от unmanaged подхода. Только сериализация и десериализация данных.
Re[8]: C# generics: Тормоза. Что я не так делаю?
От: TK Лес кывт.рф
Дата: 11.01.05 19:56
Оценка:
Hello, "Павел Кузнецов"
>
> А можно ли через BlockCopy копировать побайтно в массив IntWrapper[]? Что-то я не нашел гарантий работоспособности этого способа в стандарте C#. По первому впечатлению попахивает использованием для аналогичных целей memcpy в C++ со всеми вытекающими.

Тут в отличии от memcpy гарантирутеся, что нельзя "пройтись" по объектным ссылкам. т.е. если это структура полями которой выступают примитивные типы, то по большому счету не так страшно, что туда запишется... приложение это не разрушит.
Posted via RSDN NNTP Server 1.9
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: TK Лес кывт.рф
Дата: 11.01.05 19:56
Оценка:
Hello, "Павел Кузнецов"
>
>> Достаточно странное поведение. Но здесь может срабатывать GC поэтому желательно делать по несколько тестов.
>
> Несколько запусков? Так и делалось. Результаты варьируются в достаточно небольших пределах. Во всяком случае разброс не влияет на "рейтинг" результатов относительно друг друга.
>

Можно делать не несколько запусков, а несколько последовательных проходов без выгрузки приложения и взятия среднего (в идеале несколько первых проходов можно считать загрузовчными и игнорировать). Что-же касается влияния GC, то пытаться подстроиться под то, что-бы он не вызывался — несколько странно... В реальном приложении он-же вызываться будет
Да и для подобного теста его влияние должно быть минимальным — вот, если делать на IntWrapper, а ObjectWrapper ...

> Честно говоря, больше всего напрягла необходимость дублировать код для каждого из тестов из-за отсутствия С++-подобных шаблонов без ограничений, присущих generics. Думаю, может, переписать тест на C++/CLI? Заодно увидим, может, там оптимизация удачнее


С этого и надо было начитать Вроде как даже в VS2003 компилятор для MC++ оптимизировать умеет...
Posted via RSDN NNTP Server 1.9
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[11]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 11.01.05 23:46
Оценка:
TK,

>> Вопрос, будет ли запись корректной? В данном случае это, конечно, побоку, но тем не менее? Как-то не нравится "выжимать" такты за счет использования конструкций, корректность работы которых не гарантирована


> Тут есть некоторая двойственность. Думаю, что сама запись корректна, но вот то, что операция sizeof является unsafe это смущает


Ага, спасибо. Просто из чтения стандарта у меня сложилось ощущение, что даже не гарантируется, что sizeof(int) == sizeof(IntWrapper):

For all other types < не из перечисленного чуть выше набора >, the result of the sizeof operator is implementation-defined and is classified as a value, not a constant.

<...>

The order in which members are packed into a struct is unspecified.

For alignment purposes, there can be unnamed padding at the beginning of a struct, within a struct, and at the end of the struct. The contents of the bits used as padding are indeterminate.

When applied to an operand that has struct type, the result is the total number of bytes in a variable of that type, including any padding.

Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[7]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.01.05 01:41
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> А считывать в буффер byte[] , из которого через BlockCopy копировать в массив. Вопервых нетипизированный ArrayList это боксинг и унбоксинг,затраты памяти фрагментация памяти.


Семантика нарушается. У него потенциально можно грузить int-ы в массив произвольного теста. Хотя конечно для теста это пофигу, а использовать ArrayList здесь просто садизм.

Я бы извернул нечто вроде:
    private T[] CreateArray(string fileName)
    {
        using (BinaryReader reader =
            new BinaryReader(File.Open(fileName, FileMode.Open)))
        {
            long len = reader.BaseStream.Length / 4;
            T[] array = new T[len];

            for (int i = 0; i < len; i++)
                array[i] = CreateElement(reader.ReadInt32());

            return array;
        }
    }

уж если нужно как ПК хочет.
... << RSDN@Home 1.1.4 beta 3 rev. 273>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[12]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.01.05 01:41
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Ага, спасибо. Просто из чтения стандарта у меня сложилось ощущение, что даже не гарантируется, что sizeof(int) == sizeof(IntWrapper):


Не, это то работает. Только все способы обойти систему типа не безопасны. Так что если тебе нужно надежное решение, то вот так будет относительно обобщенно и быстро:
private T[] CreateArray(string fileName)
{
    using (BinaryReader reader =
        new BinaryReader(File.Open(fileName, FileMode.Open)))
    {
        long len = reader.BaseStream.Length / 4;
        T[] array = new T[len];

        for (int i = 0; i < len; i++)
            array[i] = CreateElement(reader.ReadInt32());

        return array;
    }
}

хотя начерта нужно обобщения для чтения встроенных типов фиксированного размера?

Ну, а самый быстрый и простой способ будет такой:
// Объявления констант для функции CreateFile
enum DESIRED_ACCESS : ulong
{
    GENERIC_READ = 0x80000000L,
    GENERIC_WRITE = 0x40000000L,
    GENERIC_EXECUTE = 0x20000000L,
    GENERIC_ALL = 0x10000000L,
};
enum SHARE_MODE : ulong
{
    FILE_SHARE_READ = 0x00000001,
    FILE_SHARE_WRITE = 0x00000002,
    FILE_SHARE_DELETE = 0x00000004
};
enum FLAGSANDATTRIBUTES : ulong
{
    FILE_ATTRIBUTE_READONLY = 0x00000001,
    FILE_ATTRIBUTE_HIDDEN = 0x00000002,
    FILE_ATTRIBUTE_SYSTEM = 0x00000004,
    FILE_ATTRIBUTE_DIRECTORY = 0x00000010,
    FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
    FILE_ATTRIBUTE_ENCRYPTED = 0x00000040,
    FILE_ATTRIBUTE_NORMAL = 0x00000080,
    FILE_ATTRIBUTE_TEMPORARY = 0x00000100,
    FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200,
    FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400,
    FILE_ATTRIBUTE_COMPRESSED = 0x00000800,
    FILE_ATTRIBUTE_OFFLINE = 0x00001000,
    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
};
enum CREATION_DISPOSITION : ulong
{
    CREATE_NEW = 1, CREATE_ALWAYS, OPEN_EXISTING,
    OPEN_ALWAYS, TRUNCATE_EXISTING
};

const int INVALID_HANDLE_VALUE = -1;
const uint INVALID_FILE_SIZE = 0xFFFFFFFF;

// Объявление Win32 API-функции CreateFile
[DllImport("Kernel32.dll")]
private extern static int CreateFile(
    string lpFileName,                            // LPCTSTR - file name
    uint daDesiredAccess,                        // DWORD - access mode
    uint smShareMode,                            // DWORD - share mode
    int lpSecurityAttributes,                    // LPSECURITY_ATTRIBUTES - SD
    CREATION_DISPOSITION dwCreationDisposition,    // DWORD - how to create
    uint dwFlagsAndAttributes,                    // DWORD - file attributes
    int hTemplateFile                            // HANDLE - handle to template file
    );

[DllImport("Kernel32.dll")]
private extern static bool ReadFile(
    int hFile,                        // handle to file
    int[] IntWrapper,                    // data buffer
    uint nNumberOfBytesToRead,        // number of bytes to read
    ref uint lpNumberOfBytesRead,    // number of bytes read
    int lpOverlapped                // overlapped buffer
    );

далее просто:
uint len = (uint)new FileInfo(fileName).Length;
ReadFile(hInputFile, arrayOfIntWrapper, len, ref dwNumberOfBytesRead, 0);
... << RSDN@Home 1.1.4 beta 3 rev. 273>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.01.05 01:41
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Честно говоря, больше всего напрягла необходимость дублировать код для каждого из тестов из-за отсутствия С++-подобных шаблонов без ограничений, присущих generics. Думаю, может, переписать тест на C++/CLI? Заодно увидим, может, там оптимизация удачнее


А в чем проблема?

ЗЫ

Вообще-то твой околотестовый код можно раза в два сплющить.
... << RSDN@Home 1.1.4 beta 3 rev. 273>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 12.01.05 04:27
Оценка:
VladD2,

> ПК> Ага, спасибо. Просто из чтения стандарта у меня сложилось ощущение, что даже не гарантируется, что sizeof(int) == sizeof(IntWrapper):


> Не, это то работает. Только все способы обойти систему типа не безопасны.


Я тоже так думаю.

> Так что если тебе нужно надежное решение, то вот так будет относительно обобщенно и быстро:

>
> private T[] CreateArray(string fileName)
> {
>     using (BinaryReader reader =
>         new BinaryReader(File.Open(fileName, FileMode.Open)))
>     {
>         long len = reader.BaseStream.Length / 4;
>         T[] array = new T[len];
>
>         for (int i = 0; i < len; i++)
>             array[i] = CreateElement(reader.ReadInt32());
>
>         return array;
>     }
> }
>


Похоже на то, что и было, но немного "причесанное". В любом случае спасибо, вставлю эту версию.

> хотя начерта нужно обобщения для чтения встроенных типов фиксированного размера?


Читаются не встроенные типы: в одном из случаев читается IntWrapper.

> Ну, а самый быстрый и простой способ будет такой:

> <... простыня кода для импорта и вызова функции Win API пропущена ...>

Не, спасибо, я лучше по-простому
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 12.01.05 10:36
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>struct DirectComparer<T> : IComparer<T>

ПК> where T : IComparable<T>
ПК>{
ПК> public int Compare(T l, T r) { return l.CompareTo(r); }
ПК> public bool Equals(T l, T r) { return l.CompareTo(r) == 0; }
ПК> public int GetHashCode(T obj){ return obj.GetHashCode(); }
ПК>}

В данном коде никакого инлайна не будет (веренее теле инлайна будет вызов l.CompareTo(r) вместо реальног сравнения) . Опять наступаешь на теже грабли.
Заметь для инта у тебя сразу скорость упала


ПК>struct DelegateComparer<T> : IComparer<T>

ПК> where T : IComparable<T>
ПК>{
ПК> public int Compare(T l, T r) { return compare_element_(l, r); }
ПК> public bool Equals(T l, T r) { return compare_element_(l, r) == 0; }
ПК> public int GetHashCode(T obj){ return obj.GetHashCode(); }

ПК> // private

ПК> delegate int CompareElement(T x, T y);
ПК> static CompareElement compare_element_ = delegate(T x, T y) { return x.CompareTo(y); };
ПК>}

Вот любители перегрузок. Вызов виртуальных (не инлайновых) компараторов не смущает. Или долго сделать специализацию компараторов конкретного типа, исключая виртуальность и используя инлайн????
Перегрузку операторов тебе не лень писать. Зачем такие сложности
и солнце б утром не вставало, когда бы не было меня
Re[8]: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 12.01.05 11:43
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Serginio1,


>> считывать в буффер byte[] , из которого через BlockCopy копировать в массив.


ПК>А можно ли через BlockCopy копировать побайтно в массив IntWrapper[]? Что-то я не нашел гарантий работоспособности этого способа в стандарте C#. По первому впечатлению попахивает использованием для аналогичных целей memcpy в C++ со всеми вытекающими.


По умолчанию для структур применяется LayoutKind.Sequential Поля структуру располагаются в памяти последовательно в том порядке, в котором они были описаны.
Что легко проверить через Marshal.SizeOf или вычислить размер массива в байтах через метод Buffer.
и солнце б утром не вставало, когда бы не было меня
Re[14]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.01.05 15:00
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Похоже на то, что и было, но немного "причесанное". В любом случае спасибо, вставлю эту версию.


Это и есть причесанное. Я взял твой код и выкинул не разумные моменты: убрал ArrayList, так как он приводит к боксингу (да и вообще зачем память дублировать?), вместо исключений сделал нормальную обработку конца файла, заменил неуклюжий try на using.

Могу предложить более экстровагантный способ на итераторах. Правда он будет процентов на 20% медленее. За-то красиво.

ПК>Читаются не встроенные типы: в одном из случаев читается IntWrapper.


Ну не будет подобного кода в реальных приложениях. Или ты будешь возиться с "бинарщиной" в которой будет зашит некий формат (тогда тебе один фиг писать специализированный ридер), или ты будешь читать однотипные данные (тогда опять же проще как я писал), или ты вопспользуешся одним из видов сериализации (тогда вообще, скорее всего, будет пара строк кода, так как сериализация встроена во фрэйморк). А вот так... я не видел ни одного приложения кроме тестов которое бы содержала подобный код.

ПК>Не, спасибо, я лучше по-простому


Кстати, это очень даже по простому и довольно надежно. Описывать АПИ-шные методы нужно ровно один раз. К тому же уже имеется описание. Маршалинг выделяет для промежуточного буфера защищенный участок памяти, так что если ты напортачишь, то будет максимум безобидное исключение (в отличии от IJW в МС++ где все на твоей совести). Ну, и скорость... наверное чуть выше будет.
... << RSDN@Home 1.1.4 beta 3 rev. 273>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.01.05 15:00
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

>> Прогаммисты хреновые...


ПК>Возможно. Код взят из твоего сообщения
Автор: VladD2
Дата: 25.10.04
. Функция сравнения осталась такой же, какой она была у тебя.


А ты не обратил внимание, на то что все числа в тесте целые и меленькие?

ПК> Кста, данный казус лишний раз демонстрирует, что ни язык, ни среда исполнения не могут в достаточной степени ловить проявления небрежности


Не назвал бы данную ситуацию небрежностью. Это как раз случай неверного допущения. На целочисленных данных код будет рабуотать "на ура".

А что касается защиты среды... Дык включив контроль переполнения и будет тебе счатье. Кстати, в VC есть такая опция? Я что-то с ходу не нашел.

ПК>Мне было более-менее без разницы, что именно делает тест, абы это что-то было одним и тем же, как для C#, так и для C++. Это условие, имхо, выполнялось.


Ну, вот я тебе исправил одну строчку кода и скорость сравняласть. С одной стороны это говорит, что у VC пока оптимизатор умнее, но другой, что потенциальных проблем в том чтобы сравнять дотнет и VC по качеству генерации кода нет.

ПК>Твое наблюдение о переполнении верно. Интересно только, почему для IntWrapper и int была такая разница в быстродействии при том, что по идее работа делалась та же самая...


Думаю, происки сырого оптимизатора. Все же VC уже более 10 лет, а оптимизатору дотнета 2 года, причем с дженириками он пока что вообще не встречался.

ПК>Ок, сделаем проверку "полноценной".


Кстати, тут большая вероятность того, что Шарп проиграет, так как если он не заинлайнит код (а он это может, так как код уже сложноватый для того чтобы джит с ним возился).

Так что если тебе охота поглядеть именно потенциал, то лучше бы конечно попробовать и вариант с кастом-интерфейсом.

ПК>Ну, судя по тому, что индекс в результате находился один и тот же, с хорошей долей уверенности можно предположить, что и остальные итоги вычислений одни и те же. Как бы то ни было, теперь есть новые тесты, вроде, без арифметического переполнения.


Пашь, ты меряешь на грани разумного. Здесь любая мелочь может дать разницу в два-три раза. Это уже не архитектурная разница, а борьба команд оптимизаторов.

ПК>

ПК>Единственное, просто поправить не получилось: увлекся обобщением и упрощением, пытаясь и в C# уменьшить дублирование кода

Гы. Я изучение твоего кода сразу начал с вырезания лишнего жира. Если охота могу прислать солющен. Я там еще пару вариантов компареров забубенил.

ПК> Разница немного упала, 3-кратного преимущества C++, вроде, не наблюдается, теперь разрыв "всего лишь" в 2 раза.


Ты С++-ный проект выложил бы в файлы. Да и Шарповый тоже.

ПК> Но и тесты, в общем-то несколько изменились... Также можно заметить, что теперь в C# и int тормозит почти так же, как IntWrapper. Ниже привожу новые результаты и код, по просьбам телезрителей включая C++


Ты кстати, мерил бы вот этим: Точное измерение производительности в дотнете
Автор: VladD2
Дата: 23.04.03
.

Вот результаты твоего подтюнегого кода (в секундах, Load это скорость загрузки, корректность проверяется ассертами):
C:\MyProjects\Tests\PerfPK\PerfPK\bin\Release>PerfPK.exe
Framework version: 2.0.40607.16

Start DelegateTest`1 test  ...
        Load time: 0.265829
        Unsorted array check sum: 192090977768567
        value: 67324752
                                                Sort duration:    0.83459
        Index: 1320102
        array[index]: 67324752
        Sorted array check sum: 192090977768567

Start ComparableTest`1 test  ...
        Load time: 0.2612564
        Unsorted array check sum: 192090977768567
        value: 67324752
                                                Sort duration:    0.59322
        Index: 1320102
        array[index]: 67324752
        Sorted array check sum: 192090977768567

Start AbstractClassComparerTest`1 test  ...
        Load time: 0.2588181
        Unsorted array check sum: 192090977768567
        value: 67324752
                                                Sort duration:    0.71454
        Index: 1320102
        array[index]: 67324752
        Sorted array check sum: 192090977768567

Start InterfaceComparerTest`1 test with IntWrapperItfOnClassComparer ...
        Load time: 0.2576783
        Unsorted array check sum: 192090977768567
        value: 67324752
                                                Sort duration:    0.70451
        Index: 1320102
        array[index]: 67324752
        Sorted array check sum: 192090977768567

Start InterfaceComparerTest`1 test with IntWrapperItfOnStructComparer ...
        Load time: 0.2605197
        Unsorted array check sum: 192090977768567
        value: 67324752
                                                Sort duration:    1.07454
        Index: 1320102
        array[index]: 67324752
        Sorted array check sum: 192090977768567

С++-ного проекта небыло, так чта...

А вот сам проект. Там кстати, есть вариант с загрузкой через итераторы... в файле Test нужно дефайн раскоментарить (экстравагантно ).

Кстати, ради хохмы скомпилируй тест на компиляторах отличных от VC. Думаю некоторые могут значительно слить. Особенно разные Борланды.
... << RSDN@Home 1.1.4 beta 3 rev. 273>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 12.01.05 20:43
Оценка:
VladD2,

> ПК> Читаются не встроенные типы: в одном из случаев читается IntWrapper.


> Ну не будет подобного кода в реальных приложениях.


Мне достаточно того, что по меньшей мере в данном тестовом приложении он мне понадобился. Писать два раза одно и то же для int и для IntWrapper, если этого возможно относительно легко избежать, я не собираюсь даже в тесте, тем более, если это не относится к тестируемым аспектам.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.01.05 10:42
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:


>> Вот любители перегрузок. Вызов виртуальных (не инлайновых) компараторов не смущает. Или долго сделать специализацию компараторов конкретного типа, исключая виртуальность и используя инлайн????


ПК>Кажется, я начинаю понимать. Чтобы писать относительно эффективный обобщенный код на C#, его в определенных местах нужно разбавлять копипастой


>> Перегрузку операторов тебе не лень писать. Зачем такие сложности


ПК>Что значит "сложности"? Я хочу повторно использовать один раз написанные функции, а не плодить вместо них "ускорители" В голове в качестве "жизненного" примера я держу шаблоны классов, встречавшихся в реальных проектах: "маленькие" вектора:Vector2<T>, Vector3<T>, Vector4<T> (можно еще обобщить, сделав Vector<dimension,T>); соответствующие им "маленькие" матрицы: Matrix2x2<T> и т.п. Как только начинаются сколько-нибудь существенные вычисления, там очень быстро устаешь размножать копипастой всевозможные операции с векторами и матрицами; да еще и ошибки надоедает вылавливать. Естественно, хочется все это дело один раз написать, и везде использовать. То, что видно в тестовом коде только обозначает характерные моменты.


Вообще под Net техника программирования несколько другая, чем в нативе, то что для натива хорошо то для манагед плохо. В данном случае непонятно зачем городить DirectComparer<T> , вместо специализации структуры для конечного типа struct ComparerInt : IComparer<int>
.
Техника перегрузки операторов решается через интерфейс с методами Great,Less,Equals,GreatOrEquals итд.
(при QuicSort оператора на равенство не нужно, а значит и лишние тики выигрываются). И при соответсвующей оптимизации они будут инлайнится так же как и при перегрузке операторов в шаблонах.
Ну и на худой конец еще существует и кодогенерация например R#

В любом случае интересно будет посмотреть на эти тесты после выхода релиза.
и солнце б утром не вставало, когда бы не было меня
Re[2]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 13.01.05 17:36
Оценка:
Tom,

> Ээээ. Паш а ты теперь будешь там на C#-пе писать? С плюсы как же? Не покидай нас!


Пока планов писать что-нибудь кроме тестов на C# у меня нет (разве что, если в web какой-нить занесет). (По крайней мере пока) к C# у меня интерес исключительно академический. Интересно смотреть на все (новые) языки. Просто, наконец, получилось немного с C# generics поиграться, а не только в черновиках стандарта читать. Соответственно, и вопросы появились практического характера.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[16]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.01.05 23:53
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Мне достаточно того, что по меньшей мере в данном тестовом приложении он мне понадобился. Писать два раза одно и то же для int и для IntWrapper, если этого возможно относительно легко избежать, я не собираюсь даже в тесте, тем более, если это не относится к тестируемым аспектам.


Бога ради, только не нужн делать далеко идущие выводы.
... << RSDN@Home 1.1.4 beta 3 rev. 279>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.01.05 23:53
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Мне охота посмотреть возможно ли на Шарпе эффективно писать generic код, работающий с value-типами, и если возможно, насколько легко это дается. Пока, кажется, что нелегко.


Я не сказал бы, что в этом вообще есть смысл. С точки зрения разных контейнеров — проблем особых нет. И эффективность высокая. С алгоритмами все сложенее. Абстракция в Шарпе вся динамическая и есть соттвествующие накладные расходы.

Но в рельном коде вообще по жизни приволируют классы. Уж не знаю почему, но структуры обычно вылезают там где нужна оптимизация и очень часто создают проблемы дизайна. Я не раз выкидывал структуры и заменял их на классы просто ради гибкости. Причем разницы в скорости при этом не ощущал (хотя потенциально она есть).

>> ты меряешь на грани разумного. Здесь любая мелочь может дать разницу в два-три раза.


ПК>Для C++ это не так: обычно abstraction penalty колеблется в пределах десятка процентов, если вообще присутствует.


Тут не причем С++. Ты погляди на код быстрой сортировки и прикинь, что чаще всего выполняется?... Правильно, сравнение! А так как это самая частая операция, то начинает серьезно играть роль банальная скорость вызова метода, инлайнинг, выравнивание кода и т.п. Может случиться так, что переставление переменных местами или добавление новой даст выигрыш в скорости.

Ну, и опять же. Что за зверь такой С++? Есть разные компиляторы. Один прекрасно справляется с устранением накладных расходов вызываемых абстракцией, а другой не очень. Разница тоже может составить разы.

Я еже кажется приводил пример с qsort и С. Так можно из-за того, что qsort оказывается не быстрой делать выводы о всем языке?

>> Это уже не архитектурная разница, а борьба команд оптимизаторов.


ПК>Для меня больше вопрос не в абсолютных цифрах C# vs. C++, а в относительных: насколько падает быстродействие C# для арифметики и около того при использовании мало-мальской абстракции над встроенными типами и попытках повторного использования кода. Пока результаты не радуют.


Опять та же песня. Да от оптимизатора это зависит. Ну, и опять же где ты видел подобную работу с вэйлью-типами?

ПК>Я выложил полные листинги всех файлов, которые использовал.


1. Нахрена мучиться с копированием отдельных фрагментов когда можно скачать проект в зипе?
2. Где код С++-ного проекта? Или нужно по бырому его переписать?

ПК>Для этих тестов мне вполне достаточно используемой точности.


И все же. Это намного удобнее и точнее.

ПК>Гм... В том же сообщении полный код на C++


Где?

ПК>С удовольствием, если руки дойдут.


Давай, двай. Народу интересно. Темболее, что тебе доступен больший список компиляторов.
... << RSDN@Home 1.1.4 beta 3 rev. 279>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.01.05 23:53
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Что значит "сложности"? Я хочу повторно использовать один раз написанные функции, а не плодить вместо них "ускорители" В голове в качестве "жизненного" примера я держу шаблоны классов, встречавшихся в реальных проектах: "маленькие" вектора:Vector2<T>, Vector3<T>, Vector4<T> (можно еще обобщить, сделав Vector<dimension,T>);


Пош, раскажи мне идиоту, чем List<T> тебя не устраивает? И чем он может быть медленнее чем какой-нибудь вектор?
... << RSDN@Home 1.1.4 beta 3 rev. 279>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 14.01.05 02:09
Оценка:
VladD2,

> ПК> В голове в качестве "жизненного" примера я держу шаблоны классов, встречавшихся в реальных проектах: "маленькие" вектора:Vector2<T>, Vector3<T>, Vector4<T> (можно еще обобщить, сделав Vector<dimension,T>);


> Пош, раскажи мне идиоту, чем List<T> тебя не устраивает? И чем он может быть медленнее чем какой-нибудь вектор?


Это векторы линейной алгебры. Vector2<T> — вектор двумерного пространства, Vector3<T> — трехмерного и т.п. Для этих классов определены соответствующие операции. Например, умножения на матрицу, афинные преобразования и т.п. List<T> сюда никаким боком.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[7]: C# generics: Тормоза. Что я не так делаю?
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.01.05 03:07
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Это векторы линейной алгебры. Vector2<T> — вектор двумерного пространства, Vector3<T> — трехмерного и т.п. Для этих классов определены соответствующие операции. Например, умножения на матрицу, афинные преобразования и т.п. List<T> сюда никаким боком.


Понял. Просто смутило похожее на СТЛ-евский вектор название. Что-то по матрицам я в сети встречал. Но сейчас уже не припомню.

В общем, если что никогда не поздно подключить генератор кода. Не та это проблема.
... << RSDN@Home 1.1.4 beta 3 rev. 279>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: C# generics: Тормоза. Что я не так делаю?
От: Павел Кузнецов  
Дата: 14.01.05 18:44
Оценка:
Денис,

> высказывание типа: "вот это тормозит в 2 раза" очень некорректно, и очень далеко от истины, чтобы языку присваивать клеймо — "в 2 раза медленней!".


Я нигде не говорил, что язык в 2 раза медленней (такое высказывание с моей точки зрения вообще не имеет смысла). Я измерял конкретные test cases, которые, да, примерно в 2 раза медленнее.

> (Особенно было смешно про видео-плейер.)


Это была иллюстрация того, что может означать замедление в 2 раза в ответ на совершенно конкретный вопрос Тимофея: "Два раза это так плохо?". Это не означает, что использование C# вообще или generics в частности означает обязательное замедление в 2 раза.

> На моей машине Cel2500 такие показатели:

> C++ — 46 сек.
> C# — 18 сек.
> Тогда я могу сделать однозначный вывод по поводу производительности?

Да, конечно, можно сделать однозначный вывод о том, что данный фрагмент кода на VC++ выполняется в два с половиной раза медленнее, чем на C#. Более того, можно даже объяснить, почему это происходит, и что по этому поводу можно сделать. На мой взгляд, любые шаги на пути к пониманию подобных моментов полезны.

> Линейной и тем-более константной зависимости тут нет, и быть не может


Речь идет о зависимости падения производительности от повышения уровня абстракции, т.е. в данном случае количества "слоев" использования generics и разнообразных оберток вокруг простых типов. Эта зависимость вполне может быть как константной, так и линейной, равно как и фактически любой другой. Правда, подозреваю, что дальше линейной на практике дело не пойдет.

> Ясно одно, что в достаточно крупных приложениях очень часто используется дин. память, поэтому в словах Влада истина в том, что зачастую при миграции более менее крупного проекта с C++ на C# заметно повышение производительности на глаз.


Еще раз: я не делаю никаких общих выводов. Речь о стоимости использования конкретных техник. А именно, применения generic программирования к абстракциям уровня value-классов. Производительность подсистемы управления памятью меня в данных test cases вообще не интересует, т.к. код был специально написан таким образом, чтобы по возможности исключить выделение/освобождение памяти в замеряемых фрагментах.
Posted via RSDN NNTP Server 1.9
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.