Аннотация:
После опубликования предыдущей серии тестов мы получили ряд откликов от читателей. В тесте Delphi нам справедливо указали на допущенную ошибку.
...
Мы протестировали еще три компилятора, а именно: GNU C++ 2.95.3-7 (вернее его порт на Windows — mingw), Borland C++ 5.5 и VC.Net (v 7.0) beta 2.
...
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Для ВСС можно было-бы использовать VCL-вский тип AnsiString. Для этого нужно иметь библиотеку vclie50.lib и vcl.h (может не весь, я постапвил #invlude <vcl.h>
1.16 sec for 10000000(10M)цикла (PIII-600, Win2000, BCC5.5) Немного непонятно, почему у Вас цифры другие, может цикл был больше, чем в исходнике, который я у вас скачал.
Еще. Кто-то правильно заметли, что в float тесте надо f+= поставить, а то очень просто можно соптимизировать. У меня ИСС в пять раз медленнее если f+=
Мы пользовались свободно распространяемым BCC. С ним не поставляется VCL, так что пришлось использовать std::string.
Пришлите ваш тест мы попробуем и его. Заодно попробуем вариант с CString (так легче сравнивать с VC).
float-вообще не удались. :( Но мне прислали (похоже) подходящие алгоритмы. Чуть позже мы реализуем их для всех подопытных и сделаем "Шустрика-3". За одно протестируем финальную версию .Net.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
stl в CBulder6 полностью переписана и работает ощутимо быстрее.
Для CBulder5 (BorlandC++ 5.5) в стринг-тесте просто необходимо использовать #define _RWSTD_COMPILE_INSTANTIATE перед включением sting.h. В этом виде он работает в 2(!) раза быстрее, даже быстрее чем VisualC++ 6 при использовании в нём std::string.
Не могу защитать сравнительные тесты по работе со строками в Delphi и С. Дело в том, что в C, C++, Java и иже с ними строк ВООБЩЕ НЕТ. Есть классы которые представляют текстовые данные. Такой класс можно написать и в Delphi. Именно работу с таким классом и надо сравнивать с C! "Встроенность" в язык Pascal позволяет Delphi делать то, что в С/C++/Java сделать нельзя. Можно только написать аналогию, но обычно никто этого не делает — слишком сложно. Поэтому, кстати, и появляются лазейки для хакеров. Pascal позволяет вернуть строку как результат функции. Попробуйте переписать тестовый пример для работы со строковыми функциями. Тогда и сравним.
SK>Не могу защитать сравнительные тесты по работе со строками в Delphi и С. Дело в том, что в C, C++, Java и иже с ними строк ВООБЩЕ НЕТ.
1. В Яве и C# есть *встроеный* класс строк, а значит можно считать, что строки в них есть.
2. Работать со строками приходится и в языках где встроенных строк нет.
3. Все это не важно, так как цель теста была измерить производительность кода в *наиболее стандартных* для продукта условиях.
SK>Такой класс можно написать и в Delphi.
Можно. Но за все время существования этих статей никто не дал ни одной ссылки на подобное чудо. Так как в наши цели не входило доказательство превосходства (или отставания) Дельфи, и так как обычной практикой в Дельфи является использование т.н. длинных строк, мы выбрали для теста именно их. *Потенциальные* возможности Дельфи показаны как раз в этой статье (№ 2). В этой же статье говорится почему эти результаты почти бессмысленны. Если внимательно прочесть две первые статьи, то такого предложения/вопроса просто не должно возникнуть.
SK>Именно работу с таким классом и надо сравнивать с C!
С в этой статье вообще не рассматривался. Ну, а почему мы не хотим (и не будем) сравнивать то чего нет написано выше.
SK>"Встроенность" в язык Pascal позволяет Delphi делать то, что в С/C++/Java сделать нельзя.
Абсурдность этого утверждения для меня столь очевидна, что я даже теряюсь. Delphi проиграл (!) строковый тест несмотря на всю встроенность. Ну, а C++ (как минимум) позволяет делать практически все, что угодно. Это подтверждает то, что 90% коммерческого софта сделано именно на нем.
SK>Можно только написать аналогию, но обычно никто этого не делает — слишком сложно.
Создать простой строковый класс который увеличит скорость работы со строками может практически любой программист (если не сказать больше). Так, что это заявление тоже бессмысленно. По моему никто этого не делает только по причине лени и незнания.
SK>Поэтому, кстати, и появляются лазейки для хакеров.
Ага. Именно по этому. Бред какой-то.
SK>Pascal позволяет вернуть строку как результат функции.
Я так понял, что ты уверен, что другие языки на это не способны? Можешь проверят:
Все как один рабочие (если я конечно где-то не очепятался).
SK>Попробуйте переписать тестовый пример для работы со строковыми функциями. Тогда и сравним.
С какой целью? Я уже один раз сказал, прочти внимательнее все части статьи! Там сказано почему в этом тесте все так, а не иначе.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Ни одного предложения кроме уже оговоренных в статьях в этом сообщении не было. Поэтому я и не хотел отвечать на этот вопрос. Так, что или заканчиваем этот флейм, или потрудись привести аргументированные предложения (задать вопросы) НЕ ОГОВОРЕННЫЕ В СТАТЬЕ.
Возможно в будущем мы переделаем строковый тест, но это будет не создание строкового класса для Delphi (этим пусть занимаются те кому это нужно), а изменением примера, так чтобы он стал более приближен к жизни (менее синтетическим). Например, парсинг.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
1. Я так понял, что вы действительно не понимаете разницу между понятием "вернуть строку" и "вернуть указатель на строку". Объясняю – в ваших примерах вызывающая функция должна строку освободить. Если функция универсальна, то она не может знать – освобождать память или нет. Вызов конструкции A(B(C(S))) превращается в кошмар.
2. Я также понял, что вы просто не замечаете разницы между кодом, который заранее выделяет строке некое количество байт, от кода который определяет длину строки динамически, как это делает Delphi. Когда я говорил о хакерах, я имел в виду случаи, когда использовался именно этот недостаток кода. Даже в вашем примере вы не проверили хватит ли вам 100 байт перед тем как вызывать StrCpy.
3. Вот более реальный пример в котором C# проигрывает Delphi в 7.5 раз (C# — 7:21, Delphi – 0:58).
Delphi
function Str1(var S: string): string;
begin
Result := S+' ('+intToStr(length(S))+') ';
end;
function Str2(S: string): string;
var
p: integer;
begin
p := pos('(',S);
Result := ' '+copy(S,1,p-1);
end;
procedure Test;
var
s,ss : string;
i : Integer;
begin
ss := 'Test'; s := '';
for i := 1 To 100000 do begin
s := s + trim(Str2(Str1(ss)));
end;
ShowMessage('Length = '+intToStr(length(s)));
end;
> 1. Я так понял, что вы действительно не понимаете разницу между понятием "вернуть строку" и "вернуть указатель на строку". Объясняю – в ваших примерах вызывающая функция должна строку освободить. Если функция универсальна, то она не может знать – освобождать память или нет. Вызов конструкции A(B(C(S))) превращается в кошмар.
Вы, похоже, не знакомы толком ни с Си++, ни с Дельфами.
> Я так понял, что вы действительно не понимаете разницу > между понятием "вернуть строку" и "вернуть указатель на строку".
Ну, ну. Когда строка является указателем на строку (например, в случае голого С), то "вернуть строку" и "вернуть указатель на строку" — одно и тоже. Если речь идет о CString, то конечно это разные вещи.
Похоже, что вы просто не понимаете приведенный мной код. Да еще пытаетесь обвинить меня в некомпетентности. :)
> в ваших примерах вызывающая функция должна строку освободить.
Когда как.
C#/Ява — освобождение не требуется. Строка будет жить до тех пор пока на нее есть хоть одна ссылка.
C++ — в случае прямой работы с указателями вызывающая функция должна нести ответственность за жизнь строки (памяти под нее отведенной). Естественно, что это справедливо только в случае если строка занята динамически.
Для CString производится подсчет ссылок, так что ничего вручную освобождать не нужно, а строку можно свободно передавать как возвращаемый параметр, даже через несколько функций.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
> Если функция универсальна, то она не может > знать – освобождать память или нет.
Полная чушь (извините за грубость). А как по-вашему работает COM? BSTR там именно так и живут. Есть соглашения которых придерживаются все программисты. Для упрощения контроля памяти делаются хелперы вроде CComBSTR.
> Вызов конструкции A(B(C(S))) превращается в кошмар.
В данном случае все три функции должны возвращать динамически занимаемые строки. Причем способ выделения памяти должен быть одинаковым. Освобождением ресурсов должна заниматься функция/процедура из которой происходит вызов функции A.
> 2. Я также понял, что вы просто не замечаете разницы между > кодом, который заранее выделяет строке некое количество байт, > от кода который определяет длину строки динамически, как > это делает Delphi.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
То что в Delphi нет стрингбилдера и что именно из-за этого она проигрывает в скорости, а уже говорил не раз, но что мне толку до того, что стандартный способ работы со строками в Delphi убог? Для массовой конкатенации нужно заранее резервировать память. Стрингбилдеры это и делают.
> Вот более реальный пример в котором C# проигрывает Delphi > в 7.5 раз (C# — 7:21, Delphi – 0:58).
Да медленная у вас машинка. У меня Delphi-йский вариант выполняется за 640 миллисекунд. C# действительно медленно. Но дело тут все в руках. Я уже говорил, что прежде чем писать в конфу, нужно внимательно прочитать статьи. В местах посвященных работе со строками я писал, что для конкатенации данных нужно использовать специальные классы стрингбилдеры. В средах типа Ява и .Net строки просто не рассчитаны на конкатенацию больших объемов данных. Так если в приведенном вами примере заменить основную строку на StringBuilder, то время выполнения теста у меня составляет 150 миллисекунд. Это в 4.26 раза быстрее чем на Delphi.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
В Шарповом варианте была допущена еще одна грубая ошибка. Вместо статических функций применялись методы. Это тоже вносит замедление, хотя и совсем незначительное.
Вот поправленный вариант вашего теста для C#:
using System;
using System.Text;
namespace Str2
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
string ss = "Test";
//string s = "";
DateTime d = new DateTime(0);
int iStart = Environment.TickCount;
StringBuilder sb = new StringBuilder(100000);
for(int i = 0; i < 100000; i++)
{
sb.Append(Str2(Str1(ss)).Trim());
}
int iTicks = Environment.TickCount - iStart;
Console.WriteLine("Time: " + iTicks + " ms.");
Console.WriteLine("Length: " + sb.Length);
Console.ReadLine();
}
private static string Str1(string s)
{
return s + " (" + s.Length.ToString() + ") ";
}
private static string Str2(string s)
{
int p = s.IndexOf("(");
return" " + s.Substring(0, p-1);
}
}
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.