C++ versus C#
От: c-smile Канада http://terrainformatica.com
Дата: 18.02.04 19:26
Оценка:
Навеяно спорами с VladD2 (http://www.rsdn.ru/Forum/Message.aspx?mid=538145&only=1
Автор:
Дата: 13.02.04
)
И вербально по месту с iLYA.

Два примера С# и С++ делающие следюущее:

for (int i=0; i<N; i++) 
{
  if((i & 1) == 0)
     StringBuffer1.Append("hello");
  else
     StringBuffer2.Append("hello!");
}


Результаты для 10млн итераций (тест на машине Pentium III, 850мгц):

С++ - 1692 ms (+/- 10)
С#2433 ms (+/- 50)

При этом GC в С# тестах не срабатывал. А в С++ в это время срабатывал лишний delete.

Если изменить политику переаллоцирования буфера в C++ (см ниже)
c _allocated *= 2; на, например, _allocated *= 3;
То цифры такие:
С++ - 1422 ms (+/- 10)

Вот.

Содокладчики:
Со стороны С++ — c-smile
Со стороны С# — iLYA


C#
using System;
using System.Collections;
using System.Text;
using System.IO;

namespace Benchmark_CSharp
{
    class BenchmarkCSharp
    {
        static DateTime startTime;
        static DateTime stopTime;
        static TimeSpan elapsedTime;
        
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Start C# benchmark");
            long scTime = (long)sc2( 10000000 );
            Console.WriteLine("End C# benchmark");
        }


         public static long sc2(int N)
        {
            long elapsedMilliseconds;
            startTime = DateTime.Now;

            if(N < 1) N = 1;

            StringBuilder sb1 = new StringBuilder();
                        StringBuilder sb2 = new StringBuilder();

            for (int i=0; i<N; i++) 
            {
                            if((i & 1) == 0)
                      sb1.Append("hello");
                else
                              sb2.Append("hello!");
            }

            stopTime = DateTime.Now;
            elapsedTime = stopTime.Subtract(startTime);
            elapsedMilliseconds = (int)elapsedTime.TotalMilliseconds;            
            Console.WriteLine("String Concat. (fixed) elapsed time: " + elapsedMilliseconds + " ms");

            return elapsedMilliseconds;
        }        
    }
}


C++
#include <time.h>
#include <stdlib.h>

namespace aux {

// wchar_t_buffer class - in-memory dynamic buffer.
  class wchar_t_buffer 
  {
    wchar_t*        _body;
    size_t          _allocated;
    size_t          _size;

    wchar_t *reserve(size_t size)
    {
      if((_size + size) > _allocated) 
      {
        // not bad, huh?
        _allocated *= 2;
        wchar_t *newbody = new wchar_t[_allocated];
        memcpy(newbody,_body,_size * sizeof(wchar_t));
        delete[] _body;
        _body = newbody;
      }
      return _body + _size;
    }

  public:

    wchar_t_buffer():_size(0)     { _body = new wchar_t[_allocated = 256]; }
    ~wchar_t_buffer()             { delete[] _body;  }

    const wchar_t * data()  {  
             if(_size == _allocated) reserve(1); 
             _body[_size] = 0; return _body; }

    size_t length() const         { return _size; }

    void push(wchar_t c)    { *reserve(1) = c; ++_size; }
    void push(const wchar_t *pc, size_t sz) 
    { 
      memcpy(reserve(sz),pc,sz*sizeof(wchar_t)); 
      _size += sz; 
    }
  };
}

int  main() 
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();

    int i;
  aux::wchar_t_buffer buf1;
  aux::wchar_t_buffer buf2;
    
    for (i=0; i < 10000000; i++ )
    {
    if((i & 1) == 0)
      buf1.push(L"hello",5);
    else
      buf2.push(L"hello!",6);
    }
    stopTime = clock();
    elapsedTime = (stopTime - startTime) /  (CLOCKS_PER_SEC / (double) 1000.0);
    printf("String Concat. Elapsed time: %1.0f\n", elapsedTime);//, strbuf);

  return 0;
}


C++ — VC++ 6.0
Re: C++ versus C#
От: DMach Россия http://www.1Gb.ru
Дата: 19.02.04 08:28
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Результаты для 10млн итераций (тест на машине Pentium III, 850мгц):


CS>С++ - 1692 ms (+/- 10)

CS>С#2433 ms (+/- 50)

            StringBuilder sb1 = new StringBuilder(50000000);
            StringBuilder sb2 = new StringBuilder(50000000);


String Concat. (fixed) elapsed time: 1211 ms
Pentium III 1000mhz
... << RSDN@Home 1.1.2 beta 2 >>
Re: C++ versus C#
От: alexkro  
Дата: 19.02.04 08:39
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Навеяно спорами с VladD2 (http://www.rsdn.ru/Forum/Message.aspx?mid=538145&amp;only=1
Автор:
Дата: 13.02.04
)

CS>И вербально по месту с iLYA.

CS>Два примера С# и С++ делающие следюущее:

...

А std::wstring не проверял? Кстати, компилятор надо бы поновее взять. Еще вопрос: этот примерчик можно заоптимизировать до смерти, но какой в том смысл?
Re[2]: C++ versus C#
От: c-smile Канада http://terrainformatica.com
Дата: 19.02.04 18:13
Оценка: :)
Здравствуйте, alexkro, Вы писали:

A>Здравствуйте, c-smile, Вы писали:


A>А std::wstring не проверял? Кстати, компилятор надо бы поновее взять. Еще вопрос: этот примерчик можно заоптимизировать до смерти, но какой в том смысл?


std::wstring как и String в С# — immutable classes — не предназначены для динамического склеивания строк.

Собственно оптимизирования никакого не проводилось. Такая задача не стояла.
Наоборот — работать только штатными стандартными средствами платформы.

wchar_t_buffer — исп. классичекую имплементацию динамического буфера в C++.
Это не делалось через std::vector, например, по причине наличия разных альтернативных имплементаций std::
Re[2]: C++ versus C#
От: c-smile Канада http://terrainformatica.com
Дата: 19.02.04 18:53
Оценка:
Здравствуйте, DMach, Вы писали:


DM>
DM>            StringBuilder sb1 = new StringBuilder(50000000);
DM>            StringBuilder sb2 = new StringBuilder(50000000);
DM>


Результаты для 10млн итераций c полностью преаллоцированным буфером

c#
StringBuilder sb1 = new StringBuilder(50000000);
StringBuilder sb2 = new StringBuilder(60000000);


c++

int i;
aux::wchar_t_buffer buf1(50000000);
aux::wchar_t_buffer buf2(60000000);
    
for (i=0; i < 10000000; i++ )
{
  if((i & 1) == 0)
    buf1.push(L"hello",5);
  else
    buf2.push(L"hello!",6);
}


(тест на машине Pentium III, 850мгц):

CS>>С++ - 590 ms (+/- 0)

CS>>С#1205 ms (+/- 5)

Эти результаты в общем и целом согласуются с результатами Константина Книжнижника на его реальных
имплементациях СУБД: http://www.garret.ru/~knizhnik/dybase/doc/dybase.html#comparison

Хочу еще раз подчеркнуть что данное исследование не ставит целью ответить на глупый вопрос типа "кто лучше — папа или мама?"
А просто понять какие классы задач луше делать на каждой из платформ.
Re[3]: C++ versus C#
От: Undying Россия  
Дата: 19.02.04 22:04
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>>>С++ - 590 ms (+/- 0)

CS>>>С#1205 ms (+/- 5)

CS>Хочу еще раз подчеркнуть что данное исследование не ставит целью ответить на глупый вопрос типа "кто лучше — папа или мама?"

CS>А просто понять какие классы задач луше делать на каждой из платформ.

Ты считаешь, что различия в быстродействии в 1.5 — 2 раза критично для значительного числа задач?
... << RSDN@Home 1.1 beta 2 >>
Re: C++ versus C#
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.02.04 23:28
Оценка: 3 (1) +1
Здравствуйте, c-smile, Вы писали:

CS>Навеяно спорами с VladD2 (http://www.rsdn.ru/Forum/Message.aspx?mid=538145&amp;only=1
Автор:
Дата: 13.02.04
)

CS>И вербально по месту с iLYA.

Ну, что же... отрадно, что ты перешел от заявлений к измерениям. Пускай не все получается, но все же это уже прогресс.

Итак, для начала определимся, что конкретно мы сравниваем. Сравнивать C++ и C# довольно бессмысленно, так как это всего лишь языки. Всегда можно найти плохой компилятор С++ который проиграет C#-у. Согласен?

Значит сравнивать нам нужно все же нечто иное. Я бы охарактеризовал бы это как ".NET vs. Оптимизирующие компиляторы". В качестве эталонного компилятора возьмем реализацию VC7-8 (обладающие лучшими характеристиками на сегодня). ОК? С C#-ом все еще проще, на сегодня есть только реализации от МС. Их доступно три: 1.0, 1.1 и 1.2 (Whidbey).

Теперь о том, как мы сравниваем. Сравнивать специализированную реализацию и универсальную не корректно. Если уж мы хотим получить ответ о качестве генерации кода.

Стало быть разберем оба примера по отдельности. Сначала С++-ый. Итак я создал прокт, поместил в него твой С++-код и скомпилировал его в двух вариантах: 1) для Win32 (Release), и 2) тоже с ключом /clr. Результаты оказались следующими (у меня Atlon 2100+):
Unmanaged-вариант: 751
Managed-вариант:   811

Таким образом, разница составила 7% и ее можно лекто списать на Interop возникающий при вызове unmanaged-функции memcpy (которая к тому же в unmanaged-варианте VC вообще превращается в ассемблерные инструкции).



Теперь зайдем с другого конца. Возьмем тест на C#-е и попытаемся переписать его на С++ без ручных оптимизаций. Как бы поступил средний программист встань пред ним подобная задача? Да очень просто... он воспользовался бы готовым классом. B VC предоставляет нам такой класс — это CString. Его ATL-версия как раз имеет вариант CStringW (работающий с Юникодом). Переписываем тест:

#include "stdafx.h"
#include "atlstr.h"
#include <time.h>
#include <stdlib.h>

int _tmain()
{
    double elapsedTime;
    clock_t stopTime;
    clock_t startTime = clock();

    int i;
    CStringW buf1;
    CStringW buf2;

    for (i = 0; i < 10000000; i++ )
    {
        if((i & 1) == 0)
            buf1.Append(L"hello", 5);
        else
            buf2.Append(L"hello!", 6);
    }
    stopTime = clock();
    elapsedTime = (stopTime - startTime) /  (CLOCKS_PER_SEC / (double) 1000.0);
    printf("String Concat. Elapsed time: %1.0f\n", elapsedTime);//, strbuf);

    return 0;
}



Запускаем... дожидаемся, окончания работы программы... и делаем выводы.

Выводы не утешительны. С++ вообще не пригоден для программирования.

Обман? Несомненно!

PS

Какой же вывод можно сделать из всего этого? Да очень простой. Разница в коде порождаемом JIT-ом .NET-а/Явы и кодом порождаемом оптимизирующим компилятором С++ ничтожна. Она конечно есть, и в большинстве случаев она не в пользу .NET-а, но намного больше на работу конечного приложения влияет правильность выбора алгоритма и грамотность реализации этого алгоритма.

Намек ясен?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: C++ versus C#
От: IT Россия linq2db.com
Дата: 20.02.04 01:16
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>но намного больше на работу конечного приложения влияет правильность выбора алгоритма и грамотность реализации этого алгоритма.

VD>Намек ясен?

Говорил бы уж сразу — от радиуса кривизны
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: C++ versus C#
От: c-smile Канада http://terrainformatica.com
Дата: 20.02.04 02:14
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Теперь зайдем с другого конца. Возьмем тест на C#-е и попытаемся переписать его на С++ без ручных оптимизаций. Как бы поступил средний программист встань пред ним подобная задача? Да очень просто... он воспользовался бы готовым классом. B VC предоставляет нам такой класс — это CString. Его ATL-версия как раз имеет вариант CStringW (работающий с Юникодом). Переписываем тест:...


Для тех кто в танке повторяю. class NET.String is immutable. То же самое и в stl
StringBuilder же это то что называется динамический буфер оптимизированный в первую очередь для append.
Использовать для этой цели CString/String/std::string — это даже не "средний программист", это хуже.
Или ты хочешь сказать что C# только для средних программистов?

Т.е. не надо передергивать. Работаем с динамическим буфером.

Вот исходники C#::StringBuilder::Append

Пример на С++ повторяет это с точностью до политики переаллоцирования

newCapacity = ( currentString.Capacity)*2; // To force a predicatable growth of 160,320 etc. for testing purposes

Кстати судя по скобкам там была другая формула изначально.


        // Appends a copy of this string at the end of this string builder.
        /// <include file='doc\StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append2"]/*' />
        public StringBuilder Append(String value) {
            //If the value being added is null, eat the null
            //and return.
            if (value==null) {
                return this;
            }

            int tid;
            // hand inlining of GetThreadSafeString
            String currentString = m_StringValue;
            tid = InternalGetCurrentThread();
            if (m_currentThread != tid) 
                currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity);

            int currentLength = currentString.Length;
          
            int requiredLength = currentLength + value.Length;
            
            if (NeedsAllocation(currentString,requiredLength)) {
                String newString = GetNewString(currentString,requiredLength);
                newString.AppendInPlace(value,currentLength);
                ReplaceString(tid,newString);
            } else {
                currentString.AppendInPlace(value,currentLength);
                ReplaceString(tid,currentString);
            }

            return this;
        }

        private String GetNewString(String currentString, int requiredLength) {
            int newCapacity;

            requiredLength++; //Include the terminating null.

            if (requiredLength >  m_MaxCapacity) {
                throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"),
                                                      "requiredLength");
            }
          
            newCapacity = ( currentString.Capacity)*2; // To force a predicatable growth of 160,320 etc. for testing purposes

            if (newCapacity<requiredLength) {
                newCapacity = requiredLength;
            }

            if (newCapacity> m_MaxCapacity) {
                newCapacity =  m_MaxCapacity;
            }

            if (newCapacity<=0) {
                throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
            }

            return String.GetStringForStringBuilder( currentString, newCapacity);
        }



"Намек ясен? "
Re[3]: C++ versus C#
От: IT Россия linq2db.com
Дата: 20.02.04 04:04
Оценка: :))
Здравствуйте, c-smile, Вы писали:

CS>Использовать для этой цели CString/String/std::string — это даже не "средний программист", это хуже.


Индус?
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: C++ versus C#
От: alexkro  
Дата: 20.02.04 04:31
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, alexkro, Вы писали:


A>>Здравствуйте, c-smile, Вы писали:


A>>А std::wstring не проверял? Кстати, компилятор надо бы поновее взять. Еще вопрос: этот примерчик можно заоптимизировать до смерти, но какой в том смысл?


CS>std::wstring как и String в С# — immutable classes — не предназначены для динамического склеивания строк.


Кто тебе сказал, что wstring is immutable? Ссылочку на стандарт пожалуйте.

CS>Собственно оптимизирования никакого не проводилось. Такая задача не стояла.

CS>Наоборот — работать только штатными стандартными средствами платформы.

Зачем тогда свой наворот на C++ писать? Штатное стандартное средство C++ — wstring.
Re[3]: C++ versus C#
От: naje  
Дата: 20.02.04 07:40
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, VladD2, Вы писали:


VD>>но намного больше на работу конечного приложения влияет правильность выбора алгоритма и грамотность реализации этого алгоритма.

VD>>Намек ясен?

IT>Говорил бы уж сразу — от радиуса кривизны


Т.б. у Книжника этот радиус большой?
Re[3]: C++ versus C#
От: alexkro  
Дата: 20.02.04 10:49
Оценка:
Здравствуйте, c-smile, Вы писали:

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

CS>Использовать для этой цели CString/String/std::string — это даже не "средний программист", это хуже.

CStringT конечно-же sucks для этой цели, если посмотреть на реализацию. Но вот насчет std::string ты ошибся.

CS>Или ты хочешь сказать что C# только для средних программистов?


Я почти уверен, что и C# версию можно достаточно соптимизировать. Кстати, вопрос на засыпку: чем определяется предел оптимизации для данного примерчика?
Re: C++ versus C#
От: alexkro  
Дата: 20.02.04 10:51
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>При этом GC в С# тестах не срабатывал. А в С++ в это время срабатывал лишний delete.


... который здесь никакой роли не играет .
Re[3]: C++ versus C#
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.02.04 14:00
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Для тех кто в танке повторяю.


То есть для тебя самого, что ли?

CS> class NET.String is immutable.


Видимо System.String или просто string. Ну, так про него речь и идет.

CS> То же самое и в stl


Про него тоже.

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

CS>Использовать для этой цели CString/String/std::string — это даже не "средний программист", это хуже.

Как раз CString для этого подходит. На малых объемах он довольно эффективен.

CS>Или ты хочешь сказать что C# только для средних программистов?


На С++ тоже не боги работают. И писать свой класс из-за того что нужо сканкатирировать строки большинство людей не будет. Так что рельное приложение на С++ с большой долей вероятности окажется менее эффективной.

CS>Вот исходники C#::StringBuilder::Append


CS>Пример на С++ повторяет это с точностью до политики переаллоцирования


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

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

PS

Итак, давай попробуем еще раз подумать насклько справедливы твои слова о том, что на дотнете нельзя создать конкурентно-спосбоного кода? Ведь даже если взять самые мрачные прогнозы (отстование дотнетного кода в два раза) особых проблем как-то не видно.
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: C++ versus C#
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.02.04 14:00
Оценка:
Здравствуйте, alexkro, Вы писали:

CS>>Собственно оптимизирования никакого не проводилось. Такая задача не стояла.

CS>>Наоборот — работать только штатными стандартными средствами платформы.

A>Зачем тогда свой наворот на C++ писать? Штатное стандартное средство C++ — .


Изменил в этотм тесте класс на wstring. Результат получился 1478. Против 1520 у C#.
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: C++ versus C#
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 20.02.04 14:07
Оценка:
Здравствуйте, c-smile, Вы писали:

В C# есть проблемы с GC http://www.rsdn.ru/forum/?mid=390648
Автор: Serginio1
Дата: 23.09.03
. Все остальное работает очень быстро особенно с Валуе типами.
По поводу приведенных тестов то они не корректны так как компилятор оптимизирует такие тесты до нельзя, и сравнение с компонентами Net тоже не очень. Например свои аналоги могут работать в 1.5 — 2 раза быстрее на том же C#. Скажем так нетовские компоненты не очень, усреднены, но для массового использования вполне пригодны.
Но никто не учитывает фрагментацию памяти итд.
и солнце б утром не вставало, когда бы не было меня
Re[4]: C++ versus C#
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.02.04 14:39
Оценка:
Здравствуйте, alexkro, Вы писали:

A>Я почти уверен, что и C# версию можно достаточно соптимизировать. Кстати, вопрос на засыпку: чем определяется предел оптимизации для данного примерчика?


Специализацией алгоритма и качеством инлайнинга методов. Остальные оптимизации у Шарпа и VC практически одинаковы.
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: C++ versus C#
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.02.04 23:04
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Для тех кто в танке повторяю.


Глянь на вот это голосование
Автор: VladD2
Дата: 20.02.04
Вопрос: Какие классы или функции вы используете для конкатинации строк в своих программах на С++? (не С++-ников просьба не голосовать).
. Ну, а тепрь можно обсудить кто из нас в танке и что эффективнее:
std::string s3 = s2 + s1;


Или StringBuilder.
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: C++ versus C#
От: alexkro  
Дата: 23.02.04 06:44
Оценка: +2
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, alexkro, Вы писали:


A>>Я почти уверен, что и C# версию можно достаточно соптимизировать. Кстати, вопрос на засыпку: чем определяется предел оптимизации для данного примерчика?


VD>Специализацией алгоритма и качеством инлайнинга методов. Остальные оптимизации у Шарпа и VC практически одинаковы.


Если даже применить все теоретически доступные оптимизации, то останется только одно ограничение — скорость доступа к памяти. Учитывая это, можно оценить насколько реализации использующие только стандартные компоненты далеки от идеально оптимизированного варианта. На моей машине P4 3GHz с памятью PC3200 максимальное, что я смог достичь, — это 0.45 сек. Есть основания думать, что используя SSE для прямой работы с кэшем (правильно используя на данном конкретном CPU), можно уменьшить время на 30%, что дает 0.3 сек. Теперь стандартные компоненты: std::wstring — 0.9, StringBuilder — 1.1. Отсюда вывод — стандартные комноненты достаточно хороши для данной задачи, чтобы не заботиться об специальной оптимизации (пока, конечно, это не станет основным bottleneck'ом в программе).
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.