Модераторы: Partizan, gambit

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Интересная статья, о производительности в .NET 
:(
    Опции темы
Experimenter
Дата 7.11.2007, 21:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



archeg
Цитата

Все с мелкософта кричат практически обратное тому что написано в статье
Чего кричат-то? про форич и фор?


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
archeg
Дата 7.11.2007, 21:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 612
Регистрация: 6.1.2007
Где: Киев

Репутация: 11
Всего: 27



Цитата(Experimenter @  7.11.2007,  21:04 Найти цитируемый пост)
Чего кричат-то? про форич и фор? 

Нет, я про всю статью... В общем что-то типа: "плевать на скорость, хочу удобства в написании кода и функционала"  smile 


--------------------
ИМХО задница есть универсальный интерфейс. Ибо через задницу можно сделать абсолютно ВСЕ (bash.org.ru)

Дядька всегда можно спросить в аське, если не задалбывать - не откажет smile
И вообще, на самом деле я студент, и ненавижу обращение на "Вы") Тут все свои  ;)
PM MAIL ICQ Jabber   Вверх
stab
Дата 8.11.2007, 14:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: 22
Всего: 48



Цитата(archeg @  7.11.2007,  20:46 Найти цитируемый пост)
Простой стринг - он интернованый.

Только строковые константы\литералы, остальные интернируются только при явном указании String.Intern, что делать без очень большой необходимости не надо, т.к. после вызова этого метода строка останется в памяти до выгрузки домена из памяти, даже если на неё нет больше ссылок в коде. Нет способа удалить её из пула интернированных строк.

Про статью:

1. Перечисления. Автор двоечник и мерил скорость выполнения пустого цикла, компилер выкидывает такое тело, в этом может убедиться каждый, воспользовавшись рефлектором. Я на месте компилера ещё бы и сам цикл выкидывал. Даже если автор отключил оптимизацию, то джитер всё равно тело цикла выкинет:

Код

0000000c    xor        edx,edx 
0000000e    jmp        00000013 
00000010    add        edx,1 
00000013    cmp        edx,989680h 
00000019    setl       al   
0000001c    movzx      eax,al 
0000001f    test       eax,eax 
00000021    jne        00000010


Как можно заметить, цикл пуст. -1 автору smile Правда не очень понятно почему джитер такой странный код использует для проверки на завершение цикла, почему сразу после проверки не делается jne 00000010 - большая загадка. Плюс к этому, код явно создаёт ложную зависимость по EAX, что может сильно сказаться на производительности во многих случаях. -1 джитеру.

Со своей стороны могу предложить в какой-то степени некрасивый код для честного теста оригинального метода и того, что используется мной:

Код

class Program
{
    private const uint Iterations = 100000000;

    static void Main(string[] args)
    {
        bool taskStoped = false;
        Stopwatch watch = new Stopwatch();

        TaskStatus status = 0;
        watch.Start();
        for (uint i = 0; i < Iterations; i++)
        {
            taskStoped = (status & TaskStatus.Stoped) == TaskStatus.Stoped;
            status++;
        }
        watch.Stop();
        Console.WriteLine("1: {0} ms, {1}", watch.ElapsedMilliseconds, taskStoped);

        // --

        status = 0;
        watch.Reset();
        watch.Start();
        for (uint i = 0; i < Iterations; i++)
        {
            taskStoped = (status & TaskStatus.Stoped) != 0;
            status++;
        }
        watch.Stop();
        Console.WriteLine("2: {0} ms, {1}", watch.ElapsedMilliseconds, taskStoped);

        Console.ReadLine();
    }
}

238 ms
176 ms

Мой способ короче в записи и немного быстрее. В любом случае, другие два описанных метода - жуткое убожество разума. -1 MVP, +1 автору smile

2. Циклы. Тут вроде всё по уму протестировано, но почему-то у меня разница на инициализации не 10 раз, а 4.

3. Объектно-ориентированное программирование. Бредовое, имхо, тестирование, виртуальные члены не инлайнятся, а статические инлайнятся в данном случае, т.к. объём кода мал, но, по данным со спутников-шпионов, для членов объёмом более 32-х байт в IL это не так. А вообще-то, автор опять тестирует пустой цикл для не виртуальных членов. -1 автору smile Кроме того, виртуальные и статические члены имеют разную логику работы и применяются для разных целей. Честное тестирование не должно использовать инлайн и должно иметь "switch" при вызове статических членов - доморощенный полиморфизм.

Без инлайна, т.к. это наиболее вероятный вариант, например если метод выкидывает исключения, то, на данный момент, он не инлайнится, из-за того, что начинаются проблемы с раскруткой стека в случае исключений. Так же, у меня есть подозрения, если код использует методы типов, которые могут кидать исключения, то инлайн так же отключается.  Таким образом, в более-менее серьёзном проекте каждый второй статический метод не инлайнится. Со "switch", т.к. это полностью реализует функциональность виртуальных членов - грубо говоря, "switch" по типу:

Код

class Program
{
    private const uint Iterations = 100000000;

    static void Main(string[] args)
    {
        Stopwatch watch = new Stopwatch();


        watch.Start();
        for (uint i = 0; i < Iterations; i++)
        {
            Static.DoWork1(i);
        }
        watch.Stop();
        Console.WriteLine("static: {0} ms", watch.ElapsedMilliseconds);

        bool callDoWork1 = true;
        watch.Reset();
        watch.Start();
        for (uint i = 0; i < Iterations; i++)
        {
            if (callDoWork1)
            {
                Static.DoWork1(i);
            }
            else
            {
                Static.DoWork2(i);
            }
            callDoWork1 = !callDoWork1;
        }
        watch.Stop();
        Console.WriteLine("static \"polymorphic\": {0} ms", watch.ElapsedMilliseconds);

        watch.Reset();
        watch.Start();
        AbstractBase obj = new Virtual2();
        for (uint i = 0; i < Iterations; i++)
        {
            obj.DoWork(i);
        }
        watch.Stop();
        Console.WriteLine("virtual: {0} ms", watch.ElapsedMilliseconds);


        Console.ReadLine();
    }

    public static class Static
    {
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static int DoWork1(uint value)
        {
            if (value == uint.MaxValue)
            {
                return 0;
            }
            else
            {
                return 1;
            }
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static int DoWork2(uint value)
        {
            if (value == uint.MaxValue)
            {
                return 0;
            }
            else
            {
                return 1;
            }
        }
    }
}

public abstract class AbstractBase
{
    public abstract int DoWork(uint value);
}

public class Virtual1: AbstractBase
{
    public override int DoWork(uint value)
    {
        throw new InvalidOperationException();
    }
}

public class Virtual2: Virtual1
{
    public override int DoWork(uint value)
    {
        if (value == uint.MaxValue)
        {
            return 0;
        }
        else
        {
            return 1;
        }
    }
}

static: 655 ms
static "polymorphic": 728 ms
virtual: 619 ms

Остальное тестировать просто не интересно, instance call = static call + this в ECX, interface call = virtual call, но немного медленней, видимо, из-за специфической инстукции в которую он в конечном счёте превращается, abstract call = virtual call, вообще не понимаю почему автор в отдельный тест его вынес.

Автор явно лукавит на счёт интерфейсов, не может быть такой разницы:
929 - интерфейсы - call dword ptr ds:[00930010h]
669 - виртуальные члены - call dword ptr [eax+38h]

У меня разница всего в 10 миллисекунд. Закрадывается вопрос, на чём же тестировал автор?

4. Файловые операции. Как это водится, всё свелось к ограничению функциональности, мог бы хотя бы знаменитый RSDN-овский бинарный форматер в пример привести, его хотя бы можно приспособить для обмена SOAP-сообщениями и не только.

"Remouting сделан для облегчения разработки клиент-серверных решений. По сути, он заменяет TCP/IP." - позабавило, как можно заменить протоколом из Application Layer протоколы из Transport Layer и Network Layer.

5. Инициализация классов. Для начинающих полезно. +1

6. Сравнение типов. И опять автор тестирует пустые циклы, все выражения в if первых двух циклов просто вырезаются при компиляции. Честное тестирование на объекте предварительно приведенном к object даёт цифры в 10 раз большие. И опять же, typeof немного другой функционал предоставляет, иногда нужно узнать точный тип, а не возможность приведения.

7. Проверка на инициализацию строки. Проверку на инициализацию строки надо делать с помощью String.IsNullOrEmpty, это выглядит куда более элегантней, избавляет от лишней писанины и к тому же не проигрывает по скорости, т.к. инлайнится.

Короче, автор двоечник. smile И подвело его банальное не понимание того, что только пустой цикл в 10 000 000 итераций может выполняться за 8 миллисекунд.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Experimenter
Дата 8.11.2007, 15:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



stab, прекрасно, не поленился, проверил, не то что я и многие тут на слово поверили аффтару.

+1

Но крупицы истины все-таки в статье есть.


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
stab
Дата 8.11.2007, 15:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: 22
Всего: 48



Цитата(Experimenter @  8.11.2007,  19:25 Найти цитируемый пост)
Но крупицы истины все-таки в статье есть. 

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


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Experimenter
Дата 8.11.2007, 17:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



Цитата

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

А можно об этом чуть поподробнее?


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
stab
Дата 9.11.2007, 12:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: 22
Всего: 48



Есть два уровня оптимизации:

1. Компилятор. Выкидывает из кода вещи которые никогда не сработают или результат работы которых нигде не используется, иногда сопровождает это варнингами, иногда нет. Например:

Код

if (Math.PI == 1.0)
{
    Console.WriteLine("OMG, Pi = 1.0");
}

Выдаст Unreachable code detected, в сборке просто не будет этого if и его тела.

Код

for (int i = 0; i < 100; i++)
{
    int sum = i + i;
}

Тут никаких сообщений не будет, но тело цикла будет пустым в сборке. Сам цикл оставляют видимо из-за того, что есть такая штука как busy-waiting loops. Хотя, честно говоря, я не очень понимаю зачем это делается - может просто страшно удалить полностью. smile

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

2. Джитер. Тут начинается самое интересное. Как всем известно, джитер занимается переводом IL-кода в машинный, заявлено, что делает он это учитывая особенности процессора на котором всё это осуществляется. На самом деле, довольно сложно наткнуться на такой специализированный код, по большей части расширенные инструкции применяются для копирования больших структур и работы с Int64 и Double, да и то очень редко. Ни о какой автоматической векторизации вычислений даже речи не идёт, а жаль.

Вся сила джитера, на данный момент, в инлайне. Например:

Код

Console.WriteLine(Divide(4, 2));

static int Divide(int dividend, int divisor)
{
    if (divisor == 0)
    {
        throw new ArgumentOutOfRangeException("divisor");
    }
    else
    {
        return dividend / divisor;
    }
}

После обработки джитером это будет просто Console.WriteLine(2), т.е. джитер статически вычислил значение Divide(4, 2) и заменил его на результат - 2. Во многих случаях он этого конечно не сможет сделать, например если бы я получал параметры Divide с консоли, то этого бы не произошло. Вывод: джитеру надо помогать. smile Как можно меньше исключений, как можно меньший объём кода, как можно меньше динамики.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Experimenter
Дата 9.11.2007, 12:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



stab, удовлетворен ответом на все 100


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
stab
Дата 9.11.2007, 13:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: 22
Всего: 48



.. забыл упомянуть один тонкий момент. В последнем примере, если бы не было выброса исключения ArgumentOutOfRangeException и было бы динамическое чтение параметров с консоли, то джитер заинлайнил бы машинный код метода Divide перед вызовом Console.WriteLine, но так как метод может выбросить исключение, этого не происходит.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.1490 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.