Ниша для Дельфи [Re: [python] медленные операции со строками
субъективное имхо - на Delphi разработка программ ведётся быстрее.
Ну свойство языка такое что ли. тем более, что паскаль легче для понимания, чем любой другой компилируемый язык
![](/images/graemlins/smile.gif)
Deplhi - язык для небольших проектов, которые будут работать на достаточно слабых машинах, в том числе, которые тянут только Win9x. Для Москвы это неактуально, зато за счет этого Delphi жив в остальной России.
Что ты, в данном случае, понимаешь под RAD? визуальная разработка desktop приложений?
> Ну свойство языка такое что ли. тем более, что паскаль легче для понимания, чем любой другой компилируемый язык
для понимания кем? программистом-профи? или человеком, которому надо программировать от случая к случая?
ps
как же vb и c#?
понимание - имеется в виду вникание в чужой код.
так что ты под этим понимаешь?
и почему ты считаешь, что дельфи это умеет?
а я намекаю на то, что 10 лет назад - это было более-менее правда, а сегодня - уже нет, т.к. задачи и понимание, как эти задачи можно быстро решать - изменилось, а дельфи почти не изменилась.
delphi будет долго жить в России скорее по историческим причинам.
Теперь буду вместо слова "лепка" говорить RAD.
![](/images/graemlins/smile.gif)
Окстись! На вин98 дотнетовские приложения прекрасно работают.
![](/images/graemlins/grin.gif)
А что, с этим какие-то проблемы?
Тормозит.
хз, но на первых пеньках с 32 метрами памяти простейшие проги на .net шевелятся ооочень медленно.
И время загрузки весьма ощутимо. Даже P2-233, который вполне тянет даже XP, .net переносит плохо.
А если ngen"ом компильнуть?
Тоже не очень здорово. Для старых машин действительно лучше Delphi, чем .net. Пообщавшись с разработчиками у себя в городе, осознал, что есть ситуации, в которых .net нафиг не нужен.
Это как?
На дельфи писать очень неудобно - говноязык-с =)
Это как?Просто. Мне, например, давно понятно, что быструю сложную программу можно написать только на С++. Все C#, Java, и прочее имеют свойство отжирать память и снижать скорость работы при росте проекта. Поэтому грамотным решением для серьёзных проектов является написание некритичных к скорости работы вещей и лёгких интерфейсов на простом языке (C#, Java, WEB) и native код или модули на С++ в остальных случаях.
На до-диез писать жутко неудобно - говно язык-с.
Почему ты так думаешь? Проверял? Вообще, насколько я знаю, GC как правило работает _быстрее_ ручного удаления на большом количестве объектов.
Вот мне очевидно, что программа на С++ имеет свойство жрать всё больше времени на отлов глюков при росте проекта - до такой степени, что сложную программу становится вообще невозможно написать имея достаточно ограниченные ресурсы (человековремя) - и её скорость при этом уже никого не волнует =)
Типа, я довольно долго писал на дельфе, и единственное, что было в ней удобнее шарпа - это крайне мощная библиотека визуальных компонентов, гораздо менее завязанная на винАПИ. Например, там у каждого контрола можно было ручками указать, какой у него будет бордер - прямо размеры в пикселях на каждую из полосок.
На дельфях ничего никто уже не пишет - люди заливают себе контролы с инета и бацают на свой TForm1, а потом в проперти шитах копаются.
каждый высказал своё имхо и остался при своём.
2 асет: что значит "не пишут"? херню какую-то непонятную сказал
"Не пишут" - это когда логику своего приложения люди пишут не на языке, а в дизайнере.
не думаю, что на серверном приложении много дизайнерской работы.
Что касается С++, то да, новички на этом языке будут писать плохо и наплодят багов, которые потом будет нелегко исправить. Впрочем, те же новички только в путь наделают мемори ликов в Джаве. Аккуратный код на С++ написать можно, и он будет работать. Плюс ко всему на С++ у тебя всегда тонкий контроль за производительсностью.
Что значит проверял?Проверял в каких случаях и насколько шарп медленнее плюсов. Ну, там, написал код на шарпе, измерил производительность, переписал на плюсах, измерил, сравнил.
Я в упор не понимаю твоего утверждения "Все C#, Java, и прочее имеют свойство отжирать память и снижать скорость работы при росте проекта". GC при росте проекта работает быстрее и даже, наверное, позволяет жрать меньше памяти - вроде ты с этим согласился. Что тормозит-то? Время начальной загрузки разве что, но с этим можно эффективно бороться засовывая сборки в GAC (тогда код же кешируется, правда?) и используя ngen.
Я, конечно же, абсолютно согласен, что при необходимости можно и нужно выносить тайм-критикал куски кода в плюсовые дллки или писать их в unmanaged коде (вот по поводу этого у меня есть жуткие сомнения, кстати, как-то жутко неудобно получается).
Я в упор не понимаю твоего утверждения "Все C#, Java, и прочее имеют свойство отжирать память и снижать скорость работы при росте проекта". GC при росте проекта работает быстрее и даже, наверное, позволяет жрать меньше памяти - вроде ты с этим согласился. Что тормозит-то? Время начальной загрузки разве что, но с этим можно эффективно бороться засовывая сборки в GAC (тогда код же кешируется, правда?) и используя ngen.
Блин, ну есть у них такое свойство. Понимаешь, просто по опыту говорю. Я не задавался вопросом, почему так происходит. Это как факт для меня. Думаю, что GC при росте проекта никак не начинает работать лучше, с чего вдруг? А тормозит, кстати, всё. От загрузки данных из БД до ГУИ.
А тормозит, кстати, всё. От загрузки данных из БД до ГУИ.при достаточном (~2Гб) размере памяти можно запускать без проблем около 4-5 диалоговых окошек
![](/images/graemlins/smile.gif)
Пройтись профайлером слабо, мб все дело в том, что в пару мест Dispose забыли вызвать. Да, конечно, у .NET проблемы с холодным стартом, но в большинстве случаев вполне приемлемо. А если говорить про "большие проекты", то наверно там производительность определяется в первую очередь архитектурой: как эффективно IO организован, кэширование, как ГУИ с вычислительной частью взаимодействует и т.д. И как мне кажется в С# это делать удобнее, взять хотя бы работу с потоками. При нормальной архитектуре, потом будет достаточно довести производительность в отдельных частях кода. Причем всегда при желании можно довести до уровня unmanaged кода, для которого кстати тоже требуется тюнинг, вопрос только во времени и потраченных усилиях -- различных средств для этого в CLR предостаточно, можно и на С++ неверифицируемый код над фиксированными большими блоками памяти написать.
![](/images/graemlins/grin.gif)
>> Все C#, Java, и прочее имеют свойство отжирать память и снижать скорость работы при росте проекта.
Почему ты так думаешь? Проверял? Вообще, насколько я знаю, GC как правило работает _быстрее_ ручного удаления на большом количестве объектов.
Почему ты так думаешь? Проверял? Код -- в студию!
Почему ты так думаешь? Проверял? Код -- в студию!Почему Fj так думает? Или я? Если он, то в принципе там правда написана. Языки с GC умеют создавать и удалять объекты очень быстро. Простым new/delete на С++ ты их не обгонишь.
А если говорить про "большие проекты", то наверно там производительность определяется в первую очередь архитектурой: как эффективно IO организован, кэширование, как ГУИ с вычислительной частью взаимодействует и т.д.Блин, ну продолжать спорить на эту тему - это значит мне придётся доказывать, что проект пишут не дураки. Не охота.
![](/images/graemlins/smile.gif)
Может быть. Есть идея, что основные проблемы в том, что в прогах под .NET много чужого кода, начиная самого фреймворка. Поэтому все значительно усложняется.
#include <stdio.h>
#include <windows.h>
class Test1
{
};
class Test2
{
public:
int a;
};
class Test3
{
public:
double b1;
double b2;
double b3;
double b4;
double b5;
double b6;
double b7;
double b8;
};
void main
{
int baseTime = timeGetTime;
for (int i = 0; i < 10000000; i++)
{
Test1* t1 = new Test1;
Test2* t2 = new Test2;
Test3* t3 = new Test3;
delete t1;
delete t2;
delete t3;
}
printf("%d", timeGetTime - baseTime);
};
using System;
public class Test1
{
}
public class Test2
{
public int a;
}
public class Test3
{
public double b1;
public double b2;
public double b3;
public double b4;
public double b5;
public double b6;
public double b7;
public double b8;
}
public class Zzz
{
public static void Main
{
DateTime baseTime = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
Test1 t1 = new Test1;
Test2 t2 = new Test2;
Test3 t3 = new Test3;
}
Console.WriteLineDateTime.Now - baseTime).Milliseconds);
}
}
c:\Programming>csc testCS.cs
Microsoft (R) Visual C# .NET Compiler version 7.10.6310.4
for Microsoft (R) .NET Framework version 1.1.4322
Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.
c:\Programming>testCS.exe
562
c:\Programming>cl testCS.cpp /G7 /IC:\PROGRA~1\MICROS~2.NET\Vc7\include /IC:\PRO
GRA~1\MICROS~2.NET\Vc7\PLATFO~1\Include /link /LIBPATH:C:\PROGRA~1\MICROS~2.NET\
Vc7\PLATFO~1\Lib /LIBPATH:C:\PROGRA~1\MICROS~2.NET\Vc7\lib winMM.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.
testCS.cpp
Microsoft (R) Incremental Linker Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.
/out:testCS.exe
/LIBPATH:C:\PROGRA~1\MICROS~2.NET\Vc7\PLATFO~1\Lib
/LIBPATH:C:\PROGRA~1\MICROS~2.NET\Vc7\lib
winMM.lib
testCS.obj
c:\Programming>testCS.exe
7824
c:\Programming>
562мс против 7824мс
И это, если в шарповую версию добавить ещё GC.Collect в конец - не меняется ничего вообще.
Хохо, а пусть теперь базилио перепишет эту же прогу на дельфи - чиста сравнить скорость =)
теперь я примерно понимаю как там народ придумывал беньчмарк который показал что ява самая шустрая для кластерных вычислений
![](/images/graemlins/laugh.gif)
Вот уж не знаю, что он и откуда достаёт, но, типа, у него это получается быстро =)
как раз .Net, в отличии от нативных плюсов - гарантирует, что новые объекты будут заполнены нулями.
что-то подобное я видел в каком-то дурацком журнале от M$ - типа а вот как пиздато теперь можно бысто создавать и удалять миллион объехтов.
![](/images/graemlins/laugh.gif)
а с++ может только тупо полностью удалить объект и создать новый - если хочешь быстро - пиши свой пул.
krasin говорил что они какие-то численные расчёты делали - на С# медленнее, но не сильно.
кроме полей объекта есть ещё всякая шняга. тут обьект лежит в пуле почти готовый, а записать ему 128 байт нулей это бысто.
Я дал сурцы.
Попытайся обмануть шарповый оптимизатор (если он действительно есть) - мне что-то влом =)
Умножение/деление на шарпе, естественно, не медленнее =)
Несколько медленнее доступ к массивам (потому что range checking впрочем оптимизатору зачастую удаётся свести число проверок к минимуму.
Я даже и не знаю, что в шарпе на самом деле существенно медленнее.
Я даже и не знаю, что в шарпе на самом деле существенно медленнее.запуск
![](/images/graemlins/grin.gif)
Я ж уже говорил: GAC/nGen вроде должны решать эту проблему.
сложный inlining. Когда у нас есть куча вызовов пустых методов (может быть вложенных).
А что, sealed и internal не помогают оптимизатору оптимизить?
на Delphi он будет выполнятся раз в 10 медленнее, чем на С++.
Однако ж здесь не обсуждается производительность ЯП
![](/images/graemlins/wink.gif)
Наглая ложь!
Ты хоть бы цифры выбирал.
---
...Я работаю антинаучным аферистом...
имхо, sealed и internal - это намного большее зло, чем проблемы с производительностью
зато вот тут C++ отошёл покурить 6700 vs 5300:
#include <stdio.h>
#include <windows.h>
#include <string>
using std::string;
class Test4
{
public:
string s;
};
void main
{
int baseTime = timeGetTime;
int j;
Test4* t4;
for (int i = 0; i < 1000000; i++) // 1e6 ! FJ's 1e7
{
t4 = new Test4;
for(j=0;j<i%100;j++) t4->s+="a";
delete t4;
}
printf("%d", timeGetTime - baseTime);
};
Delphi:
{$APPTYPE CONSOLE}
uses SysUtils;
type
Test4 = class
public
s: string;
end;
var
i,j : Integer;
dt : TDateTime;
T4 : Test4;
begin
dt:=Now;
for i:= 0 to 1000000 do // 1e6 ! FJ's 1e7
begin
T4:=Test4.Create;
for j:=0 to i mod 100 do t4.s:=t4.s+'a';
T4.Free;
end;
dt:=Now-dt;
writeln('Dt:',dt*60*60*24*1000:6:2,'ms');
end.
Зло - кривые ручки разработчика, который sealed и internal прилепил не туда.
А так это безусловное добро, и не только из-за производительности =)
что скажет Fj на это в C#?
cs - 13015,625 (кстати, там нужно было поменять Milliseconds na TotalMilliseconds, впрочем, это ни на что не влияло в предыдущем тесте
cs с заменой test4 на StringBuilder - 3343,75 (ну типа конкатенация строк тормозная, потому что)
public class Test5
{
public StringBuilder s = new StringBuilder;
};
3406,25 - никакой существенной разницы типа.
![](/images/graemlins/smile.gif)
int _tmain(int argc, _TCHAR* argv[])D:\Programming\PoolObjects\release>PoolObjects.exe
{
int baseTime = timeGetTime;
char* t1 = (char*)malloc(sizeof(Test1) * 10000000);
char* t2 = (char*)malloc(sizeof(Test2) * 10000000);
char* t3 = (char*)malloc(sizeof(Test3) * 10000000);
int i;
for (i = 0; i < 10000000; i++)
{
::newvoid*t1+i*sizeof(Test1 Test1;
::newvoid*t2+i*sizeof(Test2 Test2;
::newvoid*t3+i*sizeof(Test3 Test3;
Test1*t1+i*sizeof(Test1->~Test1;
Test2*t2+i*sizeof(Test2->~Test2;
Test3*t3+i*sizeof(Test3->~Test3;
}
printf("Time = %d, objects = %d", timeGetTime - baseTime, i);
free(t1);
free(t2);
free(t3);
return 0;
}
Time = 0, objects = 10000000
Описание счётчика цикла в списке "необходимых ингридиентов" сосёт!
зы: cpp с использованием string.append - 8580, ЛОЛ.
Дык ведь о том и речь, что память фрагментируется. Я же специально три объекта взял, а не один. Это как бы на реальную жизнь всё-таки немножко похоже.
Ну ты пойми, если этих объектов создаётся мало и они создаются редко - C# не выиграет С++, т.к. итак время незаметное. Если их создаётся много или часто - на С++ делают пул и опять побеждают любой другой язык. Т.к. этот другой язык опять-таки делает пул для объектов, только универсальный.
Но вообще спор немножко не о том был =) При желании на плюсах можно и GC написать в общем-то.
Тест нечестный. У тебя наверно ни одного сбора объектов второго поколения. На практике все гораздо хуже.
Да я понимаю и не спорю. Но в среднестатистической программе на С++ объекты не создаются так же часто, как на C#/Java, потому что вместо этого используются т.н. value типы.
Тест нечестный. У тебя наверно ни одного сбора объектов второго поколения. На практике все гораздо хуже.Для честного теста надо ставить честные задачи и решать их.
![](/images/graemlins/smile.gif)
Ну ты пойми, если этих объектов создаётся мало и они создаются редко - C# не выиграет С++, т.к. итак время незаметное. Если их создаётся много или часто - на С++ делают пул и опять побеждают любой другой язык. Т.к. этот другой язык опять-таки делает пул для объектов, только универсальный.Просто признай, что для объектного стиля программирования, когда создается много небольших объектов GC работает очень неплохо. Фактически выделение приближается по скорости к выделению в стэке, плюс на сбор мусора, который зависит от суммарного размера объектов. Т.о. маленькие объекты обходятся очень дешево.
![](/images/graemlins/wink.gif)
Да я понимаю и не спорю. Но в среднестатистической программе на С++ объекты не создаются так же часто, как на C#/Java, потому что вместо этого используются т.н. value типы.С value типами гораздо сложнее, сложнее управлять временем жизни объектов, необходимо отслеживать кто работает с объектом, кто и когда его удаляет. Например, массив объектов (в смысле С++) можно удалить только тогда, когда есть полная уверенность, что никто не будет пользоваться указателями на объекты массива. Получается, что повсеместно в С++ программе выполняется оптимизации, которая очевидно усложняет проектирование и разработку.
В C# тоже для оптимизации работы GC необходимо тщательно отслеживать как создаются объекты, используются и удаляются объекты. Только это можно делать на последнем этапе разработки, и только до уровня, которого будет достаточно, чтобы получить необходимую производительность.
Это ещё что, как-то давно я серьёзно заморочился вопросом: если у меня есть массив каких-то объектов, а поверх него некая логическая структура, типа дерево - вот как лучше ссылки на элементы массива хранить, в виде ссылок или в виде индексов?
В результате я довольно долго ффтыкал на время, за которое срабатывает GC.Collect, не собравший ни одного объекта из определённой структуры. Психика моя существенно пошатнулась. Но зато ответ на первоначальный вопрос я нашёл - по времени разницы практически нет (точнее, непонятные флуктуации эффективно сводят её на нет).
В C# тоже есть value types, прикинь!
Хохо, а у меня реальная частота 1.99МГц =)
В C# тоже есть value types, прикинь!Прикинь, я это знаю.
![](/images/graemlins/smile.gif)
например, есть следующие хорошие стандартные паттерны/"правила хорошего поведения":
1. переменная объявляется только в том блоке, где используется
2. переменная используется только под одну задачу
3. освобождение локально захваченного ресурса происходит автоматически при любых обстоятельствах
в C++ - это делается через конструктор/деструктор, в C# - через IDisposable + using
4. одиночный "функционал" должен прописываться в одном месте, а не быть раскиданными по десятку файлов
C++/Дельфи - Требуют наличия предварительного описания классов/методов
5. прозрачная удобная работа с модулями - как со статически прилинкованными, так и с динамическими.
и все эти "требования" не в пользу Дельфи
А если есть, то по пункту 3 дельфи лучше C++ однозначно, т.к. там помимо деструкторов ещё и try/finally есть.
![](/smiles/clap.gif)
![](/images/graemlins/laugh.gif)
весьма показательный пример, я бы сказал.
1. переменная объявляется только в том блоке, где используетсяОсобенность языка С (и всех его потомков) не является несомненным удобством.
2. переменная используется только под одну задачуНичто не мешает это делать в Delphi.
3. освобождение локально захваченного ресурса происходит автоматически при любыхкак уже сказали, в Delphi есть и конструкторы-деструкторы.
обстоятельствах
в C++ - это делается через конструктор/деструктор, в C# - через IDisposable + using
4. одиночный "функционал" должен прописываться в одном месте, а не быть раскиданными по десятку файлов
это как раз-таки характерно для С(++) - там для "грамотного" написания модуля нужно как минимум ДВА файла - .c(pp) и .h. В Delphi - достаточно одного (не считая файла с формой, если модуль интерфейсный)
C++/Дельфи - Требуют наличия предварительного описания классов/методовВсё-таки строгость описания позволяет избежать многих ошибок проектирования и использования.
5. прозрачная удобная работа с модулями - как со статически прилинкованными, так и с динамическими.здесь ничего не могу сказать - не совсем понимаю, какие ЯП здесь лучше/хуже других.
Почему Fj так думает? Или я? Если он, то в принципе там правда написана. Языки с GC умеют создавать и удалять объекты очень быстро. Простым new/delete на С++ ты их не обгонишь.
Мой был:
В C/C++ можно написать любой код, в том числе алгоритмически тот, который порождает GC. Обратное не верно. По этому, среди программ решающих данную задачу, самая быстрая программа на C/C++ всегда быстрее или такая же по сравнению с самой быстрой программой на языке с GC.>> Все C#, Java, и прочее имеют свойство отжирать память и снижать скорость работы при росте проекта.Почему ты так думаешь? Проверял? Код -- в студию!
Почему ты так думаешь? Проверял? Вообще, насколько я знаю, GC как правило работает _быстрее_ ручного удаления на большом количестве объектов.
Лажовый и медленный код на любом языке можно написать. Сравнивать имеет смысл только наиболее быстрые варианты решения на разных языках фиксированной задачи.
Кстати, из ручного выделения/удаления объектов не следует использование стандартных new/delete.
Как показывает практика, лучше всего перегрузить new/delete для класса, объекты которого часто создаются/удаляются на что-нибудь вроде пула объектов.
for (int i = 0; i < 10000000; i++)
{
Test1 t1;
Test2 t2;
Test3 t3;
}
а, значит грамотный программист так его и напишет. А теперь порядочный компилятор этого языка также должен выкинуть цикл, так как теперь этот цикл ничего не делает.
В случае с new/delete этот цикл компилятор С++ выкинуть не имеет права, так как он не знает какими побочными последствиями обладают new/delete (которые могут быть перегружены в другой единице компиляции). В тоже время компилятор GC-языка точно знает как устроены его операции выделения/освобождения памяти и уж он может (а по-хорошему -- должен) не только оптимизировать цикл, но и выкинуть его совсем. В твоём случае, похоже, C# породил код подобный тому, что я написал (с аллокацией/деаллокацией в стеке но почему-то сам цикл оставил.
А знание компилятора GC-языка о том, как устроено выделение/освобождение памяти, достигается невозможностью выделять/освобождать память иным (не встроенным GC) способом. Другими словами, возможность такой автоматической оптимизации в GC-языке зиждется на негибкости. В более гибком C++ компилятор не обладает возможностью провести такую оптимизацию автоматически, зато за счёт большей гибкости языка грамотный программист может написать куда более оптимальную программу решающую ту же самую задачу.
Почему приведённый тобой код -- лажовый? Да потому, что по вводу/выводу твоей программы ты
не отличишь её от
#include <stdio.h>
#include <windows.h>
int main
{
printf("%d", timeGetTime - timeGetTime;
return 0;
}
А значит, именно вот этот вариант является оптимальной реализацией на C/C++ поставленной задачи. (Если функция timeGetTime заведомо не делает ничего сомнительного вроде syscall'а или каких других побочных эффектов, то оптимально будет вообще сделать printf ("0". Вот если бы ты на вход что-то брал и выдавал бы на выход нетривиальную функцию этого входа, то printf("0") уже не решал бы ту же самую задачу -- на том же входе он не дал бы того же выхода. И тогда утверждение "на поставленной тобой задаче моё решение на C/C++ уделало твоё на C#" было бы неверно. А так, пока что -- уделало с разгромным счётом: printf("%d", timeGetTime - timeGetTime; выполняется за 0 мс, да и
for (int i = 0; i < 10000000; i++)
{
Test1 t1;
Test2 t2;
Test3 t3;
}
выполняется за стоимость пустого цикла (которая у порядочного компилятора будет 0). Даже если бы ты наполнил нетривиальным содержимым конструкторы и деструкторы Test[123], мой вариант на C++ выполнялся бы за меньшее или такое же время, как и твой вариант на С#.
Если уж хочешь протестировать производительность порождения большого числа объектов с помощью
new/delete и с помощью GC, то надо проверять наиболее общую ситуацию решаемую обоими --
непредсказуемое выделение/удаление объектов. Для этого надо
в цикле случайным образом создавать объекты и сохранять
указатели на них (их самих или ссылки на них в случае языка с GC) в отдельном контейнере;
и так же случайным образом доставать указатели из этого контейнера и удалять их (объекты в GC-языке -- просто выкидывать). При этом в контейнере на значительном протяжении запуска программы должно находиться много указателей (объектов а не по одному-два, иначе эффективнее будет выделять память в obstack'ах а не через new/delete. Чтобы работа контейнера не исказила картину, в качестве него можно взять вектор переменной длины с заранее зарезервированным местом (чтобы вектор не перераспределялся в процессе запуска) и удалять из него случайный элемент с помощью swap'а с последним и удаления последнего.
Не осознал, как ты учитываешь, что у Fj-я в конструкторах может полезная деятельность вестись?
Вот кстати код, идейно близкий к тому, что выдаст GC-язык. Только он не будет выделять память на миллион экземпляров каждого объекта, а будет скорее реюзать память, выделенную под один экземпляр каждого объекта, выделяемого и тут же уничтожаемого в теле цикла.
Кто-нибудь знает как можно просмотреть машинный код C# программы во время исполнения? Даже интересно стало как оно так быстро работает (пробовал вставлять использование полей, чтобы компилятор не мог просто выкинуть тело цикла - не помогает замедлить программу)
Поставь в студии break-point, потом Debug->Windows->Disassembly. Только собирай проект в Release-е, чтобы хоть какие-то оптимизации иметь шанс увидеть.
не является особенностью - это только в паскале и потомках, во всех остальных языках переменная объявляется во время использования.
п.2 очень тесно связан с п.1, т.к. как раз объявление во время первого использования, и позволяет быстро понять, а что именно переменная хранит.
> как уже сказали, в Delphi есть и конструкторы-деструкторы.
на сколько я помню, в дельфи деструкторы какие-то ущербные, и вроде бы не позволяют поддержать паттерн - захват локального ресурса в виде конструктора/деструктора.
> Всё-таки строгость описания позволяет избежать многих ошибок проектирования и использования.
какое отношение строгость описания имеет к тому, в скольки местах надо прописывать объявление класса?
так может ты так любишь Дельфи, просто потому что не смотрел другие языки?
В C/C++ можно написать любой код, в том числе алгоритмически тот, который порождает GC. Обратное не верно. По этому, среди программ решающих данную задачу, самая быстрая программа на C/C++ всегда быстрее или такая же по сравнению с самой быстрой программой на языке с GC.Странные рассуждения. Какой смысл городить .NET GC на С++? Плюс GC не так просто реализовать. С другой стороны в .NET всегда можно кусок написать на C++ (на неверефицируемый код нет особых ограничений). Штука в том, что как правило большая часть кода несет малый вклад в общую производительность, поэтому нет смысла перенапрягаться и писать на С++, как аналогично нет смысла писать всю прогу на асме. На практике мало кто напрягает себя переписываем даже части кода на С++, и довольствуется очевидным тюнингом в рамках С#. Что очевидно говорит о том, что часто напрягать себя написанием над С++ апи ради призрачной производительности нет смысла.
Не сознал, как ты учитываешь, что у Fj-я в конструкторах может полезная деятельность вестись?
Где учитываю? В этом
for (int i = 0; i < 10Ю000000; i++)
{
Test1 t1;
Test2 t2;
Test3 t3;
}
коде? По стандарту C++ здесь вызываются конструкторы по умолчанию -- Testi::Testi (если их нет -- они полагаются ничего не делающими) и деструкторы (если есть).
В коде printf ("0") не учитываю; но так как они находятся в том же модуле трансляции, опять же, по стандарту C++ могу их заинлайнить и выкинуть вместе с циклом, так как они ничего не делают и не имеют побочных последствий (другими словами опять же учитываю их такими как они написаны). А вообще-то я -- программист, а не компилятор, и я имею право переписать программу как считаю нужным до тех пор пока она решает ту же самую задачу.
пробовал вставлять использование полей, чтобы компилятор не мог просто выкинуть тело цикла - не помогает замедлить программу
Он выделяет память под три объекта до цикла и освобождает её после цикла. И делает это без вызова функций. А использование полей -- использованию рознь. Если поля не-volatile и использование только на чтение или только на запись или по каким-то ещё причинам вычисленное не используется за пределами цикла (не утекает в вызовы функций и т.д. то опять же можно выкинуть цикл. Или использование предсказуемо, то можно и проинтерпретировать цикл и заменить его в порождаемом коде на полученный при интерпретировании результат (или что то же самое -- скомпилировать, прогнать один раз и заменить в программе на результат прогона); существующие компиляторы не на столько круты, но между тем это никто не запрещает.
Странные рассуждения.
Обычные рассуждения -- я показал, что решения данной задачи на C/C++ содержат как подмножество решения эквивалентные решениям на языке с GC. А значит аргмаксимум пересекается с множеством решений на C/C++. По русски: если задача решается программой на языке с GC за время t(A) (A -- вход программы, то есть параметр задачи то существует программа на C/C++ решающая эту задачу за время t'(A где t' не превосходит t для любого A.
Какой смысл городить .NET GC на С++? Плюс GC не так просто реализовать.
В том то и дело, что чаще всего GC не нужен и эффективнее воспользоваться неиницилизированными, нуль-иницилизированными, и не-нуль иницилизированными статичными данными, стеком, обстеками, пулами объектов или new/delete (в порядке невозрастания эффективности). Кроме того, если так уж нужен GC под C++ его не надо писать -- реализации давно существуют. А реализовать просто -- сам это делал (на подсчёте ссылок). Вот чего действительно нельзя в C/C++ -- это ограничить программиста и не дать ему манипулировать памятью в обход GC.
С другой стороны в .NET всегда можно кусок написать на C++ (на неверефицируемый код нет особых ограничений)
В том-то и дело, что в неверифицируемом коде нет особых ограничений, то есть единственного преимущества встроенного в язык GC перед GC, реализованным на уровне библиотеки. Нет ограничения программисту использовать память только посредством GC и не портить/терять и т.д. указатели.
А исходный мой пост был именно про GC versus ручное выделение памяти, а не C# versus C/C++.
Так что хорошо, что в C# можно писать неверефицируемый код, но это не имеет никакого отношения к моим высказываниям.
Штука в том, что как правило большая часть кода несет малый вклад в общую производительность
согласен
поэтому нет смысла перенапрягаться и писать на С++, как аналогично нет смысла писать всю прогу на асме. На практике мало кто напрягает себя переписываем даже части кода на С++, и довольствуется очевидным тюнингом в рамках С#. Что очевидно говорит о том, что часто напрягать себя написанием над С++ апи ради призрачной производительности нет смысла.
Пишу на C/C++ потому что они стандартизованы и переносимы. Так уж вышло, что большинство клиентов, которым поставляется мой код (серверные решения для телекоммуникаций) живут под Solaris и linux. И так уж вышло, что приходится заниматься тонким тюнингом на C/C++. Дело в том, что мы поставляем комплексные решения (железо партнёрское, ПО -- наше а у клиентов обычно есть определённая сумма на весь комплекс. Так что чисто из меркантильных соображений, хочется ставить более дешёвое железо (благо оно не наше) и более дорогой (фиксированная сумма - меньше на железо) наш софт. А на более дешёвом железе он должен быть более производительный. А так как на нас ещё и техподдержка, надо чтобы комплекс пахал как часы, а не умирал под абонентской нагрузкой.
У меня была простая и ясная цель - выяснить, что эффективнее - ручками удалять объекты сразу как только они становятся ненужны (с использованием языка С++ или полагаться на GC языка Си-шарп, который их периодически удаляет всех скопом.
Я написал прогу, которая обманывает компилятор - типа делает вид, что она что-то делает и неспроста создаёт объекты. У меня есть основания предполагать, что оба компилятора (cl и csc) обманулись приблизительно одинаково. GC показал более чем пятнадцатикратное преимущество по производительности.
Моя прога была _модельной_, то есть аппроксимировала реальную программу, в которой создаются объекты. Если кому-то кажется, что это была плохая модельная прога, которая плохо аппроксимировала работу реальных программ, он может заняться написанием своей модельной проги и сравнить быстродействие. Лучше не просто рандомно создавать объекты, а что-нибудь посчитать, типа какое-нибудь дерево построить и всё такое. Мне лично влом заниматься этим достаточно бессмысленным членомерством, а потом ещё и убеждать насильников, что их двухтысячестрочную прогу (с реализованным специализированным GC, пулингом и всем таким) не стОит считать "модельной" и сравнивать с стострочной шарповой.
"Оптимизировать" мою модельную прогу пулами или тотально удаляя "бессмысленный" код - идиотизм, простите.
Моя прога была _модельной_, то есть аппроксимировала реальную программу, в которой создаются объекты. Если кому-то кажется, что это была плохая модельная прога, которая плохо аппроксимировала работу реальных программ, он может заняться написанием своей модельной проги и сравнить быстродействие.Прога, если честно, была плохая. А писать свою никто не будет, все и так понимают, что язык с GC создаёт и удаляет объекты намного быстрее, чем тупые вызовы new/delete в C++. Просто так же многие понимают, что C++ даже при создании и удалении объектов может работать быстрее C# или другого языка с GC. Другое дело, есть ли необходимость писать на С++, что несомненно труднее, чем писать на C#.
GC показал более чем пятнадцатикратное преимущество по производительности.
Ты показал только, что существует программа на C++ которая делает тоже самое, что и программа на C#, но выполняется в 15 раз дольше. Из этого не следует, что ту же самую задачу нельзя решить на C++ (или C#) быстрее. Более того, тебе код, который делает то же самое, что и твой (за исключением того, что он под каждый объект выделяет новую память, а твой -- скорее всего реюзает память выделенную под один экземпляр объекта каждого типа.
Моя прога была _модельной_, то есть аппроксимировала реальную программу, в которой создаются объекты.
В тот-то и дело, что твоя программа не аппроксимирует ту, в которой объекты создаются и удаляются непредсказуемо. В твоей программе всё выделение и освобождение памяти -- предсказуемо. Более того, оно там стековое -- последний созданный объект удаляется первым и эта стековость вписывается в control-flow программы. Ну так и GC в этом случае и грамотный программист C/C++ будет создавать эти объекты в стеке. Только ненормальный будет использовать new/delete там где это не нужно. Итого, твоя программа на C# аппроксимирует такую на C/C++:
for (int i = 0; i < 10000000; i++)
{
Test1 t1;
Test2 t2;
Test3 t3;
}
а не такую:
for (int i = 0; i < 10000000; i++)
{
Test1* t1 = new Test1;
Test2* t2 = new Test2;
Test3* t3 = new Test3;
delete t1;
delete t2;
delete t3;
}
Моя программа делает то же самое, что и твоя, но выделяет память не в куче, а в стеке. Быстрее работает, чем на C#? Проверь. По моим представлениям -- раз в сто быстрее твоего варианта на C++ и то, если оптимизатор оставил цикл (то есть что-то нетривиальное в конструкторах происходило, хотя бы asm volatile ("". Моё ручное выделение памяти ни в чём не уступает GC.
Кстати, какие к чёрту пулы в этой программе? Где ты видишь здесь у меня пулы?
Моё утверждение было о том, что иногда на C/C++ эффективнее перегрузить new/delete для класса (например отвести под объекты этого класса пул чем пользоваться глобальным new/delete, решающим наиболее общие задачи. Но если можно обойтись выделением в стеке, как в данном случае, ни один здравомыслящий человек не будет писать пул объектов, а обойдётся стеком. Если бы создание/уничтожение объектов в твоём примере оставаясь стековым не вписывалось бы в control-flow программы, воспользовались бы obstack'ами libc, в которых выделение/освобождение памяти столь же эффективно, как в стеке программы. И только если бы создание/уничтожение объектов было бы непредсказуемым, воспользовались бы new/delete.
все и так понимают, что язык с GC создаёт и удаляет объекты намного быстрее, чем тупые вызовы new/delete в C++
Согласен
Просто так же многие понимают, что C++ даже при создании и удалении объектов может работать быстрее C# или другого языка с GC.
Абсолютно согласен
Другое дело, есть ли необходимость писать на С++, что несомненно труднее, чем писать на C#.
У меня необходимость есть, я уже писал -- требования переносимости и производительности.
Оставить комментарий
Dasar
> то, что Delphi мёртв говорить преждевременно.ниша - у Дельфи сейчас какая-то очень непонятная.
т.е. совсем непонятно:
1) на какой класс задач, приложений, людей - нацеливается дельфи.
2) чем в п. 1 дельфи лучше, чем остальные средства разработки