Re[38]: Об эффективности программ
От: Sinclair Россия https://github.com/evilguest/
Дата: 31.10.05 11:03
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Все верно, кто же спорит. Но возвращают строку отдельно, а длину отдельно. Вот если бы эта функция string STL вернула, другой разговор был бы.

Не, давай не будем придуриваться. Отсутствие в винапи нормального аналога строки не оправдание. Тебе дают буфер+длину. Если ты выкидываешь информацию о длине и пересчитываешь ее заново, то винапи не виноват.
PD>А так — придется этот, между прочим, char* буфер, передать string констуктору, который эту длину перевычислит.
Хреновый значит конструктор.
PD>А какие способы для ввода в ЭВМ текстовой информации вообще существуют ?
Да много способов есть. Банальный тайпинг, сканирование, штрих-сканирование...
А в чем, собственно, смысл? Из клавиатуры, кстати, строки вообще не вылезают, если на то пошло. Скан коды от нее приходят, и более ничего. И преобразование последовательности скан кодов в осмысленную информацию — дело промежуточного слоя. Вот мы рассмотрели в качестве такого слоя виндовый Edit. Ой-ой-ой! Он, оказывается, хранит у себя длину строки, вот нехороший какой. Не занимается он отслеживанием нуля в конце своего буфера. Отчего и лимит на размер строки в нем присуствует.
Кто у нас там еще умеет скан-коды в строки преобразовывать? Ах да, консоль. Конечно, в наш просвещенный век большая часть информации попадает в компьютер исключительно через Фар. Который, кстати, совершенно не факт, что использует именно консольный ввод. Не видал исходников, так что не уверен.

PD>Еще раз — вспомни, из-за чего весь сыр-бор разгорелся. Дарней мне привел пример, где, по его мнению, string быстрее. Я ему объяснил, что string еще сконструировать надо из сырых данных, а это char*.

Вот-вот. А я тебе рассказываю, что char* — это убогость, которая оправдания почти не имеет. Ты намеренно игнорируешь остальные компоненты возвращаемых значений. Эдак ты и на string будешь плеваться, уверяя, что ты хотел CString, а его иначе как через char* из string не получишь.
PD>Если ты мне можешь показать место в вин32, где возвращают уже сконструированные объекты классов string (или другие) — признаю свою неправоту.
Я тебе показал то место, откуда можно взять сразу готовый объект строки без пересчета длины. Покажи мне теперь то место в винапи, где есть только char*, а длины нету. Не бойся — оно есть
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[39]: Об эффективности программ
От: Pavel Dvorkin Россия  
Дата: 31.10.05 11:10
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Все верно, кто же спорит. Но возвращают строку отдельно, а длину отдельно. Вот если бы эта функция string STL вернула, другой разговор был бы.

S>Не, давай не будем придуриваться. Отсутствие в винапи нормального аналога строки не оправдание. Тебе дают буфер+длину. Если ты выкидываешь информацию о длине и пересчитываешь ее заново, то винапи не виноват.

В WinAPI не нормального аналога строки нет. В WinAPI класса строки нет. Это С интерфейс в основе своей (об OLE я не говорю). Так что не придкривайся сам — ты все отлично понмаешь.

PD>>А так — придется этот, между прочим, char* буфер, передать string констуктору, который эту длину перевычислит.

S>Хреновый значит конструктор.

А напиши получше. Сам. Который эту строку и длину примет.

S>А в чем, собственно, смысл? Из клавиатуры, кстати, строки вообще не вылезают, если на то пошло. Скан коды от нее приходят, и более ничего. И преобразование последовательности скан кодов в осмысленную информацию — дело промежуточного слоя. Вот мы рассмотрели в качестве такого слоя виндовый Edit. Ой-ой-ой! Он, оказывается, хранит у себя длину строки, вот нехороший какой. Не занимается он отслеживанием нуля в конце своего буфера. Отчего и лимит на размер строки в нем присуствует.

S>Кто у нас там еще умеет скан-коды в строки преобразовывать? Ах да, консоль. Конечно, в наш просвещенный век большая часть информации попадает в компьютер исключительно через Фар. Который, кстати, совершенно не факт, что использует именно консольный ввод. Не видал исходников, так что не уверен.

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

S>Вот-вот. А я тебе рассказываю, что char* — это убогость, которая оправдания почти не имеет. Ты намеренно игнорируешь остальные компоненты возвращаемых значений. Эдак ты и на string будешь плеваться, уверяя, что ты хотел CString, а его иначе как через char* из string не получишь.


То же самое.
With best regards
Pavel Dvorkin
Re[39]: Об эффективности программ
От: Pavel Dvorkin Россия  
Дата: 31.10.05 11:13
Оценка:
Здравствуйте, alexeiz, Вы писали:

A>std::string имеет конструктор, принимающий два итератора. Передаёшь в него указатели на начало и конец строки и никакого перевычисления длины не происходит. А ещё есть конструктор, который передаётся указатель на начало и длина.


Только при этом не забудь. что строка не должна быть в стековой памяти и т.д.
With best regards
Pavel Dvorkin
Re[40]: Об эффективности программ
От: WolfHound  
Дата: 31.10.05 11:35
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

A>>std::string имеет конструктор, принимающий два итератора. Передаёшь в него указатели на начало и конец строки и никакого перевычисления длины не происходит. А ещё есть конструктор, который передаётся указатель на начало и длина.

PD>Только при этом не забудь. что строка не должна быть в стековой памяти и т.д.
Откуда ты это взял?
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: Об эффективности программ
От: WolfHound  
Дата: 31.10.05 11:54
Оценка: +2
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Кстати, об эффективности программ, требованиям к памяти и т.д. Наткнулся вот на это

PD>http://www.rsdn.ru/Forum/Message.aspx?mid=1460511&amp;only=1
Автор: squiz
Дата: 28.10.05

PD>Почитайте весь тред, не пожалеете.
Ну читал и дальше что?
1)У меня решарпер кушает на много меньше памяти на проекте который побольше будет.
2)Если ты думаешь что будь решарпер написанан на С++ он бы жрал меньше ресурсов то ты заблуждаешься. Ибо он память не просто так жрет.
3)Плюсы которые дает решарпер значительно перевешивают его прожорливость.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[40]: Об эффективности программ
От: Sinclair Россия https://github.com/evilguest/
Дата: 31.10.05 12:17
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


S>>Хреновый значит конструктор.

PD>А напиши получше. Сам. Который эту строку и длину примет.
basic_string(
   const value_type* _Ptr, 
   size_type _Count = npos, 
   const allocator_type& _Al = Allocator ( )
);

пойдет? Типичный RTFM. И это я плохо плюсы знаю. Уж много лет на них не пишу ничего.
Смотрим в RTFM дальше.
CStringT(
   const YCHAR* pch,
   int nLength
);

Куда еще посмотреть? Может, ты мне покажешь плюсовый класс строки, который не умеет конструироваться из буфера и длины?

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

То же самое к тебе. Я вот не понимаю, почему ты утверждаешь, что char* первичен. Это, имхо, одна из тех очевидных истин, которая оказывается неправдой.
Пока что мы топчемся на месте: ты приводишь откровенно неверные утверждения в качестве аргументов.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[41]: Об эффективности программ
От: Pavel Dvorkin Россия  
Дата: 31.10.05 13:13
Оценка: :)
Здравствуйте, Sinclair, Вы писали:

S>Смотрим в RTFM дальше.

S>
S>CStringT(
S>   const YCHAR* pch,
S>   int nLength
S>);
S>


А все туда же. Взять эту CStringT и исходники посмотреть



CSimpleStringT( const XCHAR* pchSrc, int nLength, IAtlStringMgr* pStringMgr )
{
ATLASSERT( pStringMgr != NULL );

if(pchSrc == NULL && nLength != 0)
AtlThrow(E_INVALIDARG);

CStringData* pData = pStringMgr->Allocate( nLength, sizeof( XCHAR ) );
if( pData == NULL )
{
ThrowMemoryException();
}
Attach( pData );
SetLength( nLength );
CopyChars( m_pszData, pchSrc, nLength );
}

и еще

void SetLength( int nLength )
{
ATLASSERT( nLength >= 0 );
ATLASSERT( nLength <= GetData()->nAllocLength );

if( nLength < 0 || nLength > GetData()->nAllocLength)
AtlThrow(E_INVALIDARG);

GetData()->nDataLength = nLength;
m_pszData[nLength] = 0; }


Так что имеем здесь нормальное копирование входной строки. Длина, правда, берется не из нее, поскольку в документации сказано, что

pch
A pointer to an array of characters of length nLength, not null-terminated.

Просто еще один конструктор у класса есть, вот и все. Можно и другие придумать, если угодно. От этого ничего не меняется. И строка там опять-таки хранится с завершающим нулем.
With best regards
Pavel Dvorkin
Re[41]: Об эффективности программ
От: Pavel Dvorkin Россия  
Дата: 31.10.05 13:21
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, Pavel Dvorkin, Вы писали:


WH>Откуда ты это взял?


Сорри, виноват. я просто не о том подумал. Если инициализация идет по другой строке, то, конечно, никаких проблем не будет. Я-то подумал. что речь идет не об входном объекте строки, а о входном char* буфере.
With best regards
Pavel Dvorkin
Re[25]: Об эффективности программ
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.10.05 15:06
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

VD>>Скорость может упасть координально.
S>Может. Но у меня есть глубокое подозрение, что современный процессор практически не заметит лишней инструкции, потому как:
S>1. Ему надо проверять только флаг, а не память. Ближе, чем этот флаг, для процессора ничего нету.
S>2. Переполнения достаточно редки; а это означает редкость сброса конвеера из-за ошибки предсказания.

Обожаю теоретиков. Но куда проще проверить. Я уже проверял. Вывод однозначный — checked() засунутый в "узкое горлышко" приводит к очень серьезному падению ироизводительности.

Хорошим примером где checked() мог бы пригодиться является создание компоратора. В приципе функцию CompareTo() для целых числе можно писать как "x — y". Но при переполнении получается задница. Если бы checked() работал действительно быстро, то можно было бы писать как-то так:
int CompareTo(int other)
{
    try
    {
        return checked(_value - other._value);
    }
    catch (OverflowException ex)
    {
        if (_value > other._value)
            return 1;

        if (_value == other._value)
            return 0;
            
        return -1;
    }
}

Но это только в теории. На практике это приводит к офигительным торомозам. Хотя теоритически красивая оптимизация. Кстати, об оптимизациях.

S>Таким образом, даже выполнение проверки в цикле будет скорее всего тратить меньше времени, чем выборка следующего аргумента из памяти. Конечно, надо тестировать.


От именно!

S>Но хороший тест написать — это время надо, а у меня его мало


Да, уж. Строчишь сообщения аки полемет, а на пару строк кода времени нет. Ладно, мне проще тест накатать:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

class Program
{
    static void Main()
    {
        // если раскоментарить строку ниже, то результат координально изменяется
        // чем это можно объяснить я не понимаю. :( 
        //Console.WriteLine("Environment.Version " + Environment.Version);

        unchecked
        {
            Trace.Assert(CheckedCompareTo(1, 2) < 0);
            Trace.Assert(UncheckedCompareTo(1, 2) < 0);
            Trace.Assert(CheckedCompareTo(2, 1) > 0);
            Trace.Assert(UncheckedCompareTo(2, 1) > 0);
            Trace.Assert(CheckedCompareTo(1, 1) == 0);
            Trace.Assert(UncheckedCompareTo(1, 1) == 0);

            Unchecked();
            Checked();
            Unchecked();
            Checked();
            Unchecked();
            Checked();
            Unchecked();
            Checked();
        }
    }

    const int Count = 10000000;

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void Unchecked()
    {
        Stopwatch timer = new Stopwatch();

        timer.Start();

        for (int i = 1; i < Count; i++)
        {
            UncheckedCompareTo(Count / 2, i);
            UncheckedCompareTo(i, Count / 2);
            UncheckedCompareTo(i, i);
        }

        Console.WriteLine("Время UncheckedCompareTo() " + timer.Elapsed);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void Checked()
    {
        Stopwatch timer = new Stopwatch();
        
        timer.Start();

        for (int i = 1; i < Count; i++)
        {
            CheckedCompareTo(Count / 2, i);
            CheckedCompareTo(i, Count / 2);
            CheckedCompareTo(i, i);
        }

        Console.WriteLine("Время CheckedCompareTo()   " + timer.Elapsed);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static int CheckedCompareTo(int x, int y)
    {
        try
        {
            return checked(x - y);
        }
        catch (OverflowException)
        {
            if (x > y)
                return 1;

            if (x == y)
                return 0;

            return -1;
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static int UncheckedCompareTo(int x, int y)
    {
        if (x > y)
            return 1;

        if (x == y)
            return 0;

        return -1;
    }
}

Результат:
Время UncheckedCompareTo() 00:00:00.1652819
Время CheckedCompareTo()   00:00:00.2136092
Время UncheckedCompareTo() 00:00:00.1409324
Время CheckedCompareTo()   00:00:00.2139755
Время UncheckedCompareTo() 00:00:00.1367955
Время CheckedCompareTo()   00:00:00.2105921
Время UncheckedCompareTo() 00:00:00.1406726
Время CheckedCompareTo()   00:00:00.2126683


S>С этим я тоже согласен. Особенно я готов сказать "хрен с ним" потере в 2%.


К сожалению это может оказаться 50%, и в самом узком месте. Хотя конечно пожно как раз это узкое место и оптимизировать с помощью unchecked.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Об эффективности программ
От: Andy77 Ниоткуда  
Дата: 31.10.05 18:27
Оценка: :)
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>А если использовать StringBuilder, то ведь потом нужно копировать в string и в итоге 2 прохода все же ?

S>Нет. Там в большинстве случаев никакого копирования нету. UTSL, т.е. Reflector.
S>Код, который я привел, должен приводить к практически идеальному результату. Можно потестировать, что быстрее — подсчитывать суммарную длину или нет — в зависимости от количества строк в IEnumerable. Но это в любом случае эффекты второго порядка малости.

Посмотрел я на это дело рефлектором, чуда, естественно, не произошло, получится два прохода:

public override string ToString()
{
      string text1 = this.m_StringValue;
      int num1 = this.m_currentThread;
      if ((num1 != 0) && (num1 != StringBuilder.InternalGetCurrentThread()))
      {
            return string.InternalCopy(text1);
      }
      if ((2 * text1.Length) < text1.ArrayLength)
      {
            return string.InternalCopy(text1);
      }
      text1.ClearPostNullChar();
      this.m_currentThread = 0;
      return text1;
}
Re[42]: Об эффективности программ
От: Pavel Dvorkin Россия  
Дата: 01.11.05 02:52
Оценка:
Подумал еще раз и решил, что я не уделил достаточного внимания своей аргументации, и к сожалению, ввязался в софистический спор. Нет ничего сложнее, чем доказывать то, что тебе кажется вполне очевидным. Попробую начать сначала.

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

Наверное, интерфейс, использующий некоторый объект, использует его не только на выходе, но и на входе. Иными словами, коль скоро он возвращает строки с длиной, то должен бы и на входе такие строки принимать, не так ли ?

Но — не принимает. Нет практически функций винапи, которые принимали бы на входе что-то, отличное от TCHAR*. Исключения есть — та же TextOut, к примеру, да и то в нее длина не для этого передается, а для того, чтобы можно было не всю строку вывести.

Кстати, на уровне чуть пониже такие объекты действительно есть. Я имею в виду Nt/Zw функции. Вот они, действительно, и принимают, и возвращают UNICODE_STRING, в которой, действительно, есть длина, а строка не обязательно заканчивается нулем. Иногда это прорывается и в винапи, например, Lsa функции.

А теперь вернемся к выходу от винапи. Действительно, тампочти всегда можно получить длину. Может, она там и хранится, в том же эдите, может, нет, неважно. Но то, что ее можно получить — не означает, что она там есть как часть инкапсуляции. Просто получить можно. Я в своей программе тоже, если надо, могу получить . Конечно, такой подход не соответствует принципам ООП, но никто же и не утверждает, что винапи — ОО интерфейс (я не говорю об ОЛЕ).

Вот если бы функции винапи принимали и возвращали бы объект, в котором строка неразрывно связана со своей длиной ( иными словами, без длины вообще не существует) — тогда да. Принимали бы и возвращали CString, к примеру — я бы сразу признал твою правоту. Но не принимают и не возвращают. Почему ?

Кстати, о CString. Ты не задумывался — почему он внутри себя строку все же с нулем хранит ? Вроде бы ему для внутренних дел — совсем не надо. И для работы с ним — тоже, если он уже сконструирован. Паскалевский string великолепно без этого нуля обходится, и в Шарпе, я полагаю, его тоже нет. Почему же здесь есть ? Сам догадаешься или подсказать ?

Ты меня как-то спрашивал про функции винапи, которые не возвращают длину. Я не ответил — не было времени подумать. А функций таких — сколько угодно. StrCat, к примеру. Или, еще хуже, PathCombine и другие Path- функции. И т.д. Кстати, как ты думаешь, зачем StrCat в винапи существует ? В С++ она нам не очень-то и нужна, у нас своя strcat есть ? Догадался зачем ?

Ну а что касается того, что тебе char* не нравится — отвечу твоими же словами насчет списка и массива. Где нужен список, а где массив. Где лучше иметь строку с длиной, а где — без длины. Только вот одно "но" при этом имеется. Из строки char* сделать строку с длиной мне никаких проблем не составит, даже на чистом С. Так что можешь сразу считать, что строка с длиной у меня всегда потенциально есть, и если реально понадобится — всегда будет. А вот как для объекта "строка с длиной" в том же Шарпе от длины избавиться ? . Конечно, средствами Шарпа имитировать строку с нулем несложно, только вот интерфейс для этого типа, боюсь, придется писать самому.

А зачем, собственно, я это рассказываю ? Сам ты это не знаешь, что ли ? Знаешь , конечно. А для чего тогда споришь ?

На этом, как говорили в старину, позвольте откланяться и прекратить свое участие в этой порядком мне надоевшей дискуссии. Оставляю за тобой последнее слово, если, конечно, захочешь ответить.
With best regards
Pavel Dvorkin
Re[42]: Об эффективности программ
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.11.05 05:25
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>А все туда же. Взять эту CStringT и исходники посмотреть


PD> GetData()->nDataLength = nLength;

PD> m_pszData[nLength] = 0; }
Прекрасно. У нас в руках улика! Браво, Холмс.
PD>Так что имеем здесь нормальное копирование входной строки. Длина, правда, берется не из нее, поскольку в документации сказано, что
Именно, Холмс! Длина берется не из нее! Не вы ли только что уверяли, что нам придется повторно вычислять длину?

PD>Просто еще один конструктор у класса есть, вот и все.

Именно! Что и требовалось доказать.
PD>Можно и другие придумать, если угодно. От этого ничего не меняется.
Как это не меняется? От этого утверждение

А вот чтобы из входного мира строки с длиной подавали — как правило, так не делают. А если даже и сделают, это тебе не поможет в случае со string — все равно констуктор string будет исходную строку просматривать и ноль искать. По крайней мере пока это string из STL

перестает быть правильным. И конструктор ноль не ищет, и строку нам с длиной подают. Итак, что мы имеем? Что нет оправдания супероптимальным небезопасным алгоритмам с копированием строк in-place.
PD>И строка там опять-таки хранится с завершающим нулем.
Это как раз для тех, кто любит выкидывать длину и снова ее искать
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: Об эффективности программ
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.11.05 06:33
Оценка:
Здравствуйте, Andy77, Вы писали:
Может, я чего не так понял?

public override string ToString()
{
      string text1 = this.m_StringValue;
      int num1 = this.m_currentThread;
      if ((num1 != 0) && (num1 != StringBuilder.InternalGetCurrentThread()))
      {
            return string.InternalCopy(text1);
      }
      if ((2 * text1.Length) < text1.ArrayLength)
      {
            return string.InternalCopy(text1);
      }
      text1.ClearPostNullChar();
      this.m_currentThread = 0;
      return text1;
}

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

В рассматриваемом случае ни одно из условий не выполняется.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[43]: Об эффективности программ
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.11.05 07:16
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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

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

Итак, что мы имеем? На практике мы имеем полную возможность писать не только более безопасный код, но и более эффективный, построенный на строках с заранее вычисленной длиной.

Дальнейшие рассуждения про функции, не принимающие длину, я пропускаю, как нерелевантные. Мне не надо доказывать существование в винапи какого-то класса строки с длиной. Мне достаточно показать, что основные источники строк позволяют получить эту длину без дополнительных затрат. Также не относящимися к делу я считаю все рассуждения о наличии нулевого окончания в классах строк с длиной. Они, очевидно, нужны для передачи в устаревший код, который не способен использовать уже доступную информацию о длине строки.

PD>А зачем, собственно, я это рассказываю ? Сам ты это не знаешь, что ли ? Знаешь , конечно. А для чего тогда споришь ?


Я пытаюсь опровергнуть заблуждения догматического характера. В данный момент — ровно одно: то, что окружающий нас мир полон "голых" ASCIIZ — строк.
Особенно опасны эти заблуждения тем, что они оправдывают применение небезопасных техник программирования.

Действительно, в ВинАПИ есть функции, которые не позволяют получить длину строки без доп. затрат (как правило, в таких случаях можно получить точную верхнюю границу длины строки — а это позволяет нам все еще писать безопасные, хотя и не столь эффективные, алгоритмы). Тем не менее, вполне возможно свести использование таких строк к минимуму. Вот как, к примеру, выглядит дотнетовский аналог функции PathCombine:
public static string Combine(string path1, string path2)
{
      if ((path1 == null) || (path2 == null))
      {
            throw new ArgumentNullException((path1 == null) ? "path1" : "path2");
      }
      Path.CheckInvalidPathChars(path1);
      Path.CheckInvalidPathChars(path2);
      if (path2.Length == 0)
      {
            return path1;
      }
      if (path1.Length == 0)
      {
            return path2;
      }
      if (Path.IsPathRooted(path2))
      {
            return path2;
      }
      char ch1 = path1[path1.Length - 1];
      if (((ch1 != Path.DirectorySeparatorChar) && (ch1 != Path.AltDirectorySeparatorChar)) && (ch1 != Path.VolumeSeparatorChar))
      {
            return (path1 + Path.DirectorySeparatorChar + path2);
      }
      return (path1 + path2);
}

Вряд ли он менее эффективен, чем стандартный PathCombine. И его еще можно сделать более эффективным, если немножко переписать и совместить проверку на невалидные символы с копированием строк.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Об эффективности программ
От: Cyberax Марс  
Дата: 01.11.05 07:32
Оценка:
VladD2 wrote:

> C>Нет, на Java в Kawa + FAR. Ничего, все нормально было.

> Не расказывай сказки. SP6 вышел уже после выхода других версий ОС и он
> резко поднимает требования к ресурсам. NT4 + SP6 по потреблению
> ресурсов от W2k ничем не отличается. А работать в 32 метрах было не
> фонтан и вообще без сервиспаков.

Значит не sp6, а sp4/sp5 — не помню точно. Прекрасно помню, как
постоянно перегружался из Win98 (я в ней играл) в WinNT(так как она
намного стабильнее работала). Разница в скорости между 98 и NT была
заметная, но вполне терпимая.

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[44]: Об эффективности программ
От: Pavel Dvorkin Россия  
Дата: 01.11.05 12:25
Оценка:
Здравствуйте, Sinclair, Вы писали:

Хотел было прекоатить — так не получается.

S>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Итак, ты утверждаешь, что винапи использует понятие "строка с длиной".

S>Нет. Я этого НЕ утверждаю.

>Отсутствие в винапи нормального аналога строки не оправдание. Тебе дают буфер+длину. Если ты выкидываешь информацию о длине и пересчитываешь ее заново, то винапи не виноват.


Твое ? Тогда смотри дальше.


S>Дальнейшие рассуждения про функции, не принимающие длину, я пропускаю, как нерелевантные.


И в чем их нерелевантность ? В том, что они опровергают твою точку зрения, что

>Тебе дают буфер+длину. Если ты выкидываешь информацию о длине и пересчитываешь ее заново, то винапи не виноват


Где здесь дают эту самую длину ? Ответь, пожалуйста.

Где длина строки в текстовом файле ? Где она там хранится ? Там нет и нуля, конечно — по известным тебе историческим причинам. Но ограничитель там есть. (0D0A).

Где она после того, как fgets вернула строку?

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


Если мы будем делить источники на основные и неосновные, то спор уйдет в никуда. Ты приводишь случаи, когда ее дают. Я — когда нет. Для того, чтобы ты был прав — необходимо, чтобы давали всегда. В противном случае твое утверждение необоснованно. Теорема, для которй есть хоть один опровергающий пример, опровергнута.


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


Нет. Они нужны для передачи этих строк в качестве входных аргументов этих самых винапи функций. Я, кстати, об этом написал, но ты предпочел на эту часть моего письма не ответить, а просто выкинуть ее. Можешь все же ответить?Формулирую прямо

Если в винапи используются строки с длиной, почему почти ни одной функции винапи эта длина не передается ? Почему они принимают только char* (wchar*) и вычисляют эту длину у себя внутри сами ?

И как следствие из этого — еще один вопрос

Почему, если они так делают и это правильно — когда я это делаю, это неправильно ? Их выход — мой вход, мой выход — их вход.

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


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


Тем не менее, вполне возможно свести использование таких строк к минимуму. Вот как, к примеру, выглядит дотнетовский аналог функции PathCombine:

<skipped>

S>Вряд ли он менее эффективен, чем стандартный PathCombine. И его еще можно сделать более эффективным, если немножко переписать и совместить проверку на невалидные символы с копированием строк.


Охотно допускаю, что он не менее эффективен. Но какое это к делу имеет отношение ? И винапи PathCombine можно переписать, добавив ей длины и сделав безопасной. Но этого нет. В винапи. А то, что это есть или может быть в C# — я и не спорю.
With best regards
Pavel Dvorkin
Re[45]: Об эффективности программ
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.11.05 13:35
Оценка: 6 (2) +1
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Хотел было прекоатить — так не получается.

PD>Твое ? Тогда смотри дальше.
Да.
PD>Где здесь дают эту самую длину ? Ответь, пожалуйста.
Я показывал. Павел, я уже начинаю уставать. Я попросил тебя дать примеры того, откуда берутся строки без длины. Ты привел пример с едитом — неправда, он отдает длину. Ты утверждал, что даже если бы длина была, все равно ее пришлось бы вычислять — неправда, есть невычисляющие конструкторы. Теперь мы наконец переходим к той части аргументации, которая не является банально неверной.

PD>Где длина строки в текстовом файле ? Где она там хранится ? Там нет и нуля, конечно — по известным тебе историческим причинам. Но ограничитель там есть. (0D0A).

Ну и что? Как это нам помешает получить длину? Достаточно сделать вот так:
long initialPosition;
long finalPosition;
initialPosition = ftell(stream);
char line[4000];
fgets(line, 4000, stream);
finalPosition = ftell(stream);
int len = finalPosition - initialPosition;

Вуаля! У нас есть длина строки от fgets без вычисления.
PD>Где она после того, как fgets вернула строку?
В указателе файла, с которым работала fgets.
PD>Если мы будем делить источники на основные и неосновные, то спор уйдет в никуда.PD> Ты приводишь случаи, когда ее дают. Я — когда нет. Для того, чтобы ты был прав — необходимо, чтобы давали всегда. В противном случае твое утверждение необоснованно. Теорема, для которй есть хоть один опровергающий пример, опровергнута.
Павел, именно этим и отличается академический подход к разработке программ от прикладного. С прикладной точки зрения совершенно несущественно существование источников, которые передают информацию с потерями. Важнее — возможность воспользоваться нормальными источниками. Напомню, что ты пока не дал ни одного примера источника, который бы не позволил получить информацию о длине строки, кроме консоли.

Что это означает? Что пока что полезность строк без длины сводится к программам, интенсивно работающим с консолью. Все остальные программы, получается, только выигрывают от наличия длины.

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


PD>Нет. Они нужны для передачи этих строк в качестве входных аргументов этих самых винапи функций.

Да-да. Я и говорю — см. выделение.
PD>Я, кстати, об этом написал, но ты предпочел на эту часть моего письма не ответить, а просто выкинуть ее. Можешь все же ответить?Формулирую прямо

PD>Если в винапи используются строки с длиной, почему почти ни одной функции винапи эта длина не передается ? Почему они принимают только char* (wchar*) и вычисляют эту длину у себя внутри сами ?

Значительная часть функций винапи придумана около 20 лет назад. Конечно, далеко не все из них были продуманы. В некоторых из этих функций длина просто не очень-то и нужна — вполне можно построить достаточно эффективный алгоритм и без нее. По крайней мере, достаточно эффективный в большинстве случаев. Ну вот как в PathCombine. Длина им не нужна — я и так могу спорить, что они просто тупо копируют строки одна за другой в отведенный буфер, почти как strcat. При этом они проверяют, не наступили ли на ноль в конце этого буфера, в целях безопасности. В случае передачи буфера недостаточной длины мы узнаем об этом гораздо позже, чем в случае строк-с-длиной. Но этим явно можно пренебречь — вряд ли имеет смысл оптимизировать производительность обнаружения ошибок.

PD>И как следствие из этого — еще один вопрос


PD>Почему, если они так делают и это правильно — когда я это делаю, это неправильно ?

Из неверной посылки следует все что угодно. Они так делают и это неправильно.
PD>Их выход — мой вход, мой выход — их вход.

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


PD>Хм, а ведь в том примере, с которого весь сыр-бор разгорелся (с sprintf) я же битый час доказывал, что у меня есть эта самая точная верхняя граница.

Это какая? У тебя не было никакой верхней границы. У тебя была наивная вера в существование верхней границы. А когда я говорю про точную верную границу, я говорю вот про что:

Допустим, мы воспользовались функцией fgets:

void main( void )
{
   FILE *stream;
   char line[100]; // ВНИМАНИЕ! У нас буфер размером в 100 символов

   if( (stream = fopen( "fgets.c", "r" )) != NULL )
   {
      if( fgets( line, 100, stream ) == NULL) // какое совпадение - здесь тоже n = 100
         printf( "fgets error\n" );
      else
         printf( "%s", line); // и это означает, что здесь line не длиннее 100! 
            // добавим строчку в пример MSDN:
            char line2[30];
            fgets(line2, 30, stream);
            char line3[40];
            fgets(line3, 40, stream);
      fclose( stream );
   }
}

Итак, здесь мы имеем точную верхнюю границу для суммы всех длин: она равна, очевидно, 168. Мы можем спокойно использовать для конкатенации буфер этого размера. Не потому, что где-то в БД (внешней по отношению к нашей программе!) есть размер поля, и не потому, что оператор поклялся не засыпать на пробеле, а потому, что выполняются некоторые инварианты. Функция fgets так удачно написана.

PD>Но это вызвало твою негативную реакцию. Получается, коль винапи функции могут эту границу дать или ты как-то иначе оценить можешь — им доверять можно, а если я их даю — так нельзя ? Почему ?

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

PD>Охотно допускаю, что он не менее эффективен. Но какое это к делу имеет отношение ? И винапи PathCombine можно переписать, добавив ей длины и сделав безопасной. Но этого нет. В винапи. А то, что это есть или может быть в C# — я и не спорю.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Об эффективности программ
От: VladD2 Российская Империя www.nemerle.org
Дата: 01.11.05 14:27
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Значит не sp6, а sp4/sp5 — не помню точно.


Скорее всего 3. SP4 уеж поднял требования к памяти и был сильно глючным.

C> Прекрасно помню, как

C>постоянно перегружался из Win98 (я в ней играл) в WinNT(так как она
C>намного стабильнее работала). Разница в скорости между 98 и NT была
C>заметная, но вполне терпимая.

Та же фигня. Но во времена SP5-6 я уже сидел на W2k.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[46]: Об эффективности программ
От: Pavel Dvorkin Россия  
Дата: 01.11.05 14:30
Оценка:
Здравствуйте, Sinclair, Вы писали:

PD>>Где здесь дают эту самую длину ? Ответь, пожалуйста.

S>Я показывал. Павел, я уже начинаю уставать.

Я тоже.

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


Пропустим сие утверждение.

PD>>Где длина строки в текстовом файле ? Где она там хранится ? Там нет и нуля, конечно — по известным тебе историческим причинам. Но ограничитель там есть. (0D0A).

S>Ну и что? Как это нам помешает получить длину? Достаточно сделать вот так:
S>
S>long initialPosition;
S>long finalPosition;
S>initialPosition = ftell(stream);
S>char line[4000];
S>fgets(line, 4000, stream);
S>finalPosition = ftell(stream);
S>int len = finalPosition - initialPosition;
S>


S>Вуаля! У нас есть длина строки от fgets без вычисления.


Как интересно! А откуда у тебя 4000 взялось, если не секрет? Допустить, что длина строки может быть больше, чем 4000, нельзя ? А если я тебе специально файл из одной текстовой строки сгенерирую длиной в 10 Кбайт, тогда как ?

Но не это главное. Это все так, мелочи. Не мелочи другое — эта длина тут вычисляется внутри fgets. Потому как найти длину строки в текстовом файле (вообще не говоря о программных средствах) нельзя кроме как, встав в некое смещение в этом файле, просматривать символы (байты) один за другим в поисках 0D. Нет другого способа. И кто-то это делать будет. Хоть gets, хоть ReadConsole, хоть какой-то метод из .Net 9а реально, конечно, нативный метод).

А ты в своем примере просто сыграл нга том. что fgets внутри себя, вычислив эту длину, продвинула file pointer на нее. Не она сама, конечно.

Синклер, это же задача на структуры данных. Тут дело даже не в файле вообще. При такой структуре данных нет иного способа разделить элементы, кроме как тупо побайтно проходить в поисках терминатора ? Неужели это тебе надо объяснять ? Хоть какте средства здесь используй — нет.


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


Насчет потери информации — ИМХо в данном случае чистая казуистика.
Насчет примера — я тебе только что пример с текстовыми файлами привел.
Могу и другие примеры привести, подумать надо. Навскидку — LoadString. Можешь посмотреть, как в MFC CString они ее определяют, тихий ужас, потрассируй. Идиотизм, кстати, чистейшей воды. Не знаю даже, что студентам на этот счет говорить — для WinAPI.

S>Что это означает? Что пока что полезность строк без длины сводится к программам, интенсивно работающим с консолью. Все остальные программы, получается, только выигрывают от наличия длины.


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

S>Значительная часть функций винапи придумана около 20 лет назад. Конечно, далеко не все из них были продуманы. В некоторых из этих функций длина просто не очень-то и нужна — вполне можно построить достаточно эффективный алгоритм и без нее. По крайней мере, достаточно эффективный в большинстве случаев. Ну вот как в PathCombine. Длина им не нужна — я и так могу спорить, что они просто тупо копируют строки одна за другой в отведенный буфер, почти как strcat. При этом они проверяют, не наступили ли на ноль в конце этого буфера, в целях безопасности. В случае передачи буфера недостаточной длины мы узнаем об этом гораздо позже, чем в случае строк-с-длиной.


Вот с этим вполне согласен. То. что этот интерфейс был создан, когда про ООП и не говорили — верно. То, что можно интерфейс, где строки нулем не терминируются, придумать — верно (да и зачем придумывать, см. string из pascal и C#). Более того, на тему о том, что эффективнее (ну не могу я без этого тоже готов спорить. Но притягивать за уши длину там где ее нет — честное слово, не стоит. А с винапи ситуация такова -ее в общем-то нет, по крайней мере очень часто. И чем ьебя это задевает — не понимаю. Скажи просто — интерфейс старый, ИМХО дурацкий, мне не по вкусу, я считаю, что лучше, если бы строки всегда с длиной были — да я с тобой сразу соглашусь и твое мнение вполне приму (хотя лично и не разделяю его). А ты зачем-то взялся доказывать. что в этом старом дурацком интерфейсе прямо таки везде заложены идеи а ля 2000 год. Ни к чему это, ей-богу.

А файл все же остается

PD>>И как следствие из этого — еще один вопрос


PD>>Почему, если они так делают и это правильно — когда я это делаю, это неправильно ?

S>Из неверной посылки следует все что угодно. Они так делают и это неправильно.

Вот с этим тоже соглашусь. С твоей точки зрения неправильно. ИМХО. С моей — иначе ? Имеет моя точка зрения право на существование ?

PD>>Хм, а ведь в том примере, с которого весь сыр-бор разгорелся (с sprintf) я же битый час доказывал, что у меня есть эта самая точная верхняя граница.

S>Это какая? У тебя не было никакой верхней границы. У тебя была наивная вера в существование верхней границы. А когда я говорю про точную верную границу, я говорю вот про что:

S>Допустим, мы воспользовались функцией fgets:


S>

<skipped>

S>


S>Итак, здесь мы имеем точную верхнюю границу для суммы всех длин: она равна, очевидно, 168. Мы можем спокойно использовать для конкатенации буфер этого размера. Не потому, что где-то в БД (внешней по отношению к нашей программе!) есть размер поля, и не потому, что оператор поклялся не засыпать на пробеле, а потому, что выполняются некоторые инварианты. Функция fgets так удачно написана.


Ну ей богу, несерьезно. Если у тебя из fgets эта длина определена как не более 168 — можно. Если валидатор на форме мне проверил строки (а ему сказано более 100 не принимать) — у меня наивная вера.


PD>>Но это вызвало твою негативную реакцию. Получается, коль винапи функции могут эту границу дать или ты как-то иначе оценить можешь — им доверять можно, а если я их даю — так нельзя ? Почему ?

S>Потому, что ты свое значение границы высасываешь из пальца, а винапи поддерживает с математической точностью.



Но ты опять кусок из моего постинга выкинул. Придется мне его вновь привести

Ну а что касается того, что тебе char* не нравится — отвечу твоими же словами насчет списка и массива. Где нужен список, а где массив. Где лучше иметь строку с длиной, а где — без длины. Только вот одно "но" при этом имеется. Из строки char* сделать строку с длиной мне никаких проблем не составит, даже на чистом С. Так что можешь сразу считать, что строка с длиной у меня всегда потенциально есть, и если реально понадобится — всегда будет. А вот как для объекта "строка с длиной" в том же Шарпе от длины избавиться ? . Конечно, средствами Шарпа имитировать строку с нулем несложно, только вот интерфейс для этого типа, боюсь, придется писать самому.

Прокомментируй все же это , пожалуйста.

Я понимаю, что тебе мои заботы об эффективности надоели. Но все же представь — десятки миллионов строк в ОП хранить надо. Средняя длина — 10 байт. На поле длины — 4 байта. Итого 40% уходит на накладные расходы. Так что придется сокращать размер текстовой выборки. а длины и не нужны, а если нужны — их что, получить сложно ? В любой момент strcpy к твоим услугам. Да, на это время уйдет, но пусть оптимизировать надо по памяти, а не по времени. Вот тут я эти 40% и отыграю.
With best regards
Pavel Dvorkin
Re[2]: Об эффективности программ
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 01.11.05 15:54
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Почитайте весь тред, не пожалеете.


Если бы весь софт был на уровне Решарпера, у нас бы было значительно меньше проблем.
... << RSDN@Home 1.2.0 alpha rev. 617>>
AVK Blog
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.