Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2014.05.04;
Скачать: [xml.tar.bz2];

Вниз

СТРОКИ НЕ ПОТОКОБЕЗОПАСНЫ   Найти похожие ветки 

 
DevilDevil ©   (2013-10-31 15:54) [0]

Сегодня я получил удар в сердце. Я так долго изучал Delphi, так долго вникал во внутреннее устройство System, так часто видел Interlocked команды в подсчёте ссылок у строк, что у меня даже сомнений не было в том, что строки потокобезопасны.

Но сегодня простой тест доказал мне обратное.
И удивляет даже не факт отсутствия потокобезопасности, а нагромождение Interlocked (lock ...) команд в недрах. Минус этих команд в том, что они выполняются через трафик кеша, что достаточно сильно тормозит производительность.

Извещаю общественность об этом факте не для того, чтобы расстроить. А для того, чтобы возможно вы нашли причину, почему подсчёт ссылок в потоко небезопасных строках оправдано осуществлять через Interlocked

Вот код теста, который приводит к AccessViolation или OutOfMemory
program Project1;

{$APPTYPE CONSOLE}

uses
 Windows, SysUtils, Classes;

type
 T1 = class(TThread)
   procedure Execute; override;
 end;

 T2 = class(TThread)
   procedure Execute; override;
 end;

 T3 = class(TThread)
   procedure Execute; override;
 end;

var
 S: string;
 ThreadCounter: integer=0;

const
 ITERATIONS_COUNT = 1000000;

{ T1 }

procedure T1.Execute;
var
 i: integer;
begin
 InterlockedIncrement(ThreadCounter);

 for i := 1 to ITERATIONS_COUNT do
 begin
   S := IntToStr(i);
 end;

 InterlockedDecrement(ThreadCounter);
end;

{ T2 }

procedure T2.Execute;
var
 i: integer;
begin
 InterlockedIncrement(ThreadCounter);

 for i := 1 to ITERATIONS_COUNT do
 begin
   if (i and 1 = 1) then S := ""
   else S := IntToStr(i);
 end;

 InterlockedDecrement(ThreadCounter);
end;

{ T3 }

procedure T3.Execute;
var
 i: integer;
begin
 InterlockedIncrement(ThreadCounter);

 for i := 1 to ITERATIONS_COUNT do
 begin

 end;

 InterlockedDecrement(ThreadCounter);
end;

begin
 T1.Create(false).FreeOnTerminate := true;
 T2.Create(false).FreeOnTerminate := true;

 // wait threads
 while (true) do
 begin
   Sleep(1000);
   if (ThreadCounter = 0) then break;
 end;

 Writeln("S = "", S, """);
 Readln;
end.


 
DVM ©   (2013-10-31 15:59) [1]


>  СТРОКИ НЕ ПОТОКОБЕЗОПАСНЫ
>
> DevilDevil ©   (31.10.13 15:54) 
> Сегодня я получил удар в сердце. Я так долго изучал Delphi,
>  так долго вникал во внутреннее устройство System, так часто
> видел Interlocked команды в подсчёте ссылок у строк, что
> у меня даже сомнений не было в том, что строки потокобезопасны.
>

С чего ты взял, что строки потокобезопасны? Interlocked функции там для обеспечения потокобезопасного подсчета ссылок, но никак не содержимого строки.


 
DevilDevil ©   (2013-10-31 16:02) [2]

> DVM ©   (31.10.13 15:59) [1]
> С чего ты взял, что строки потокобезопасны?

мне казалось это логичным

> Interlocked функции там для обеспечения потокобезопасного
> подсчета ссылок, но никак не содержимого строки.


вот я и спрашиваю - зачем потокобезопасный подсчёт ссылок, если сами стороки не потокобезопасны ?


 
DVM ©   (2013-10-31 16:10) [3]


> DevilDevil ©   (31.10.13 16:02) [2]


> зачем потокобезопасный подсчёт ссылок, если сами стороки
> не потокобезопасны ?

т.е как зачем? тебе пример надо?


 
DevilDevil ©   (2013-10-31 16:11) [4]

> т.е как зачем? тебе пример надо?

если строки потоконебезопасны, то подсчёт ссылок должен быть обычным
ибо так быстрее


 
DVM ©   (2013-10-31 16:14) [5]

Допустим есть поток, которому в конструктор передают строку:

Create(const S: string);

Внутри конструктора он присваивает данную строку некому полю. В дальнейшем поток намерен изменять значение этого поля. Пока никакого копирования строки пока не выполняется, т.к. const. Дальше мы фактически имеет две ссылки (одну вне потока и одну внутри класса потока) на одну и ту же строку. Нам надо знать, что как только вне потока или внутри потока попытаются изменить строку, надо тотчас создать ее копию и каждый потом пусть работает со своей копией. Так ясно?


 
DevilDevil ©   (2013-10-31 16:21) [6]

> DVM ©   (31.10.13 16:14) [5]

okey, можешь как-то в кратце сформулировать
а) в каких условиях работы со строками потокобезопасность гарантируется
б) какие методы работы со строками ошибочны при условии доступа из разных потоков


 
Palladin ©   (2013-10-31 16:24) [7]

(facepalm)


 
DVM ©   (2013-10-31 16:25) [8]


> DevilDevil ©   (31.10.13 16:21) [6]


> б) какие методы работы со строками ошибочны при условии
> доступа из разных потоков

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


 
DevilDevil ©   (2013-10-31 16:28) [9]

> пытаются изменить содержимое строки

если говорить об изменении символа строки
то разве UniqueString() не спасёт ?


 
DVM ©   (2013-10-31 16:30) [10]


> DevilDevil ©   (31.10.13 16:21) [6]


> а) в каких условиях работы со строками потокобезопасность
> гарантируется

При изменении содержимого строки на которую указывают две ссылки по одной из ссылок


 
DVM ©   (2013-10-31 16:34) [11]


> DevilDevil ©   (31.10.13 16:28) [9]


> то разве UniqueString() не спасёт ?

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


 
DevilDevil ©   (2013-10-31 16:40) [12]

> DVM ©

Можно ли переформулировать твои слова, сохраняя смысл, в:
Доступ к переменной типа string (AnsiString, WideString, UnicodeString) должен осуществляться только из инициализирующего или дочернего потока.

?


 
DevilDevil ©   (2013-10-31 16:41) [13]

(из дочернего при гарантии неизменности переменной)

Как-то не звучит


 
Kerk ©   (2013-10-31 16:44) [14]

Можно еще помедитировать над тем, почему у интерфейсов (а с недавнего времени и у объектов) счетчик ссылок потокобезопасен, хотя сами интерфейсы в общем случае ничего никому не гарантируют.


 
DevilDevil ©   (2013-10-31 16:44) [15]

Владеющий переменной типа string (AnsiString, WideString, UnicodeString) поток может свободно обращаться с переменной. Остальные потоки - только читать при гарантии неизмены из владеющего.

как такая формулировка ?


 
DevilDevil ©   (2013-10-31 16:45) [16]

> Kerk ©   (31.10.13 16:44) [14]
> Можно еще помедитировать над тем, почему у интерфейсов (а
> с недавнего времени и у объектов) счетчик ссылок потокобезопасен,
>  хотя сами интерфейсы в общем случае ничего никому не гарантируют.


вопрос из той же оперы, да


 
Kerk ©   (2013-10-31 16:46) [17]

Да уж.


 
DVM ©   (2013-10-31 16:53) [18]


> DevilDevil ©   (31.10.13 16:44) [15]

Что такое поток, владеющий строкой? Это она если полем объекта TThread является что ли? Тогда почему другие потоки не могут ее менять? Могут. Главное чтоб не одновременно.


 
DevilDevil ©   (2013-10-31 17:22) [19]

> DVM ©   (31.10.13 16:53) [18]

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

Владеющий поток?
Ну это не только поле TThread. Это может быть переменная на стеке или скажем элемент динамического массива.

По идее глобальной переменной владеет главный поток.
Но вполне по логике программы может быть заложено, что к какой-то переменной обращается строго один поток. Он в данном случае и называется мной "владеющим"


 
DVM ©   (2013-10-31 17:34) [20]


> По идее глобальной переменной владеет главный поток.

По-моему между областью видимости переменной и как ты говоришь "владением " нет никакой связи.

Вот про threadvar можно с уверенностью сказать что такой глобальной переменной поток владеет. К ней и доступа нет у других потоков (у каждого свой экземпляр).


> Это может быть переменная на стеке или скажем элемент динамического
> массива.

поясни


 
DevilDevil ©   (2013-10-31 18:00) [21]

> DVM ©   (31.10.13 17:34) [20]

> По-моему между областью видимости переменной и как ты говоришь
> "владением " нет никакой связи.


предположим есть глобальная переменная
var
 SecondThreadName: string;


тогда переменная попадает в область видимости всех работающих потоков. Но "владеющим" в моём понимании должен быть только один поток. Чаще у глобальной переменной владеющим будет главный поток. Но специфика переменной может быть такой, что её модификация будет привилегией какого-то дополнительно созданного потока.

> > Это может быть переменная на стеке или скажем элемент
> динамического массива.
>
> поясни


var
 S: string;
 Ar: array of string;
 Ar2: array of record
           Name: string;
           Offset: integer;
        end;
begin


 
DevilDevil ©   (2013-10-31 18:05) [22]

Я почему спросил про формулировку
Для того чтобы во-первых самому осознать
Во-вторых, достаточно долго мучает меня идея написать хак-библиотечку, ускоряющую работу со строками. При том, чтобы заложенная функциональность сохранилась: хоть в однопоточных приложениях, хоть в многопоточных.

Там используется очень много lock-команд. Даже там, где можно обойтись без них. Есть команды xchg [mem], reg который для процессора те же lock. Для однопоточных приложений вообще можно подсчёт ссылок вести без lock.

Кстати накидал небольшой тест, который порадовал своим "профитом".
program Project1;

{$APPTYPE CONSOLE}

uses
 Windows;

const
ITERATIONS_COUNT = 100000000;

var
 X: integer=0;

procedure test_winapi;
var
 i: integer;
begin
 for i := ITERATIONS_COUNT downto 1 do
 begin
   InterlockedExchange(X, i);
 end;
end;

procedure test_xchg;
asm
 mov ecx, ITERATIONS_COUNT
 @loop:
    mov edx, ecx
    xchg [offset X], edx
 dec ecx
 jnz @loop
end;

procedure test_simple;
asm
 mov ecx, ITERATIONS_COUNT
 @loop:
    mov edx, ecx

    // xchg [offset X], edx
    mov eax, [offset X]
    mov [offset X], edx
    mov edx, eax
 dec ecx
 jnz @loop
end;

type
 TProcedure = procedure();

procedure RunTest(const Name: string; const Proc: TProcedure);
var
 T: dword;
begin
 T := GetTickCount;
   Proc();
 T := GetTickCount-T;

 Writeln(Name, " = ", T, "ms");
end;

begin
 RunTest("winapi", test_winapi);
 RunTest("xchg", test_xchg);
 RunTest("simple", test_simple);

 Readln;
end.


Результат на i3:
winapi = 1107ms
xchg = 749ms
simple = 62 ms


 
Eraser ©   (2013-10-31 18:47) [23]


> DevilDevil ©

да практически все не потокобезопасно. меня это крайне удивляет, на фоне того, что число ядер растет. допустим в iOS невозможно нормально работать с графикой в доп. потоке. в VCL кстати, тоже самое, но выкрутиться можно хотя бы.


 
Sha ©   (2013-10-31 20:03) [24]

> DevilDevil ©   (31.10.13 18:05) [22]
> достаточно долго мучает меня идея написать хак-библиотечку,
> ускоряющую работу со строками

уже
http://blog.synopse.info/post/2009/12/20/Enhanced-Run-Time-library-for-Delphi-7


 
DevilDevil ©   (2013-10-31 21:42) [25]

> Eraser ©   (31.10.13 18:47) [23]

тут не в том дело, что всё потоконебезопасно
просто я ожидал потокобезопасной работы от строк

> Sha ©   (31.10.13 20:03) [24]

чёт я исходников не вижу...

P.S.
кстати с нашей последней дискуссии, после которой меня забанили
меня очень обрадовало твоё описание поразрядной сортировки. Я прям реально офигел.
но вот здесь вот у тебя большая ошибка в рассуждениях: http://guildalfa.ru/alsha/node/20
собственно, я об этом уже написал
твой VeryBadCompareInt и есть самый правильный способ сравнения


 
DVM ©   (2013-10-31 22:34) [26]


> DevilDevil ©

в скором времени в Delphi насколько я понимаю стоит ожидать появление неизменяемых (immutable) строк, которые и работают быстрее и проблема одновременного доступа из разных потоков для которых не стоит.


 
DevilDevil ©   (2013-10-31 22:51) [27]

> DVM ©   (31.10.13 22:34) [26]

хз
либо я не понимаю immutable
либо абракадбры (борланды) вновь идут по пути коммерции, а не производительности


 
DVM ©   (2013-10-31 22:56) [28]


> DevilDevil ©   (31.10.13 22:51) [27]


> либо я не понимаю immutable

Так их пока нет, понимать особенно нечего еще. А строки такие в C#, например.


 
Rouse_ ©   (2013-10-31 22:59) [29]


> DevilDevil ©   (31.10.13 21:42) [25]
> тут не в том дело, что всё потоконебезопасно
> просто я ожидал потокобезопасной работы от строк

Дим, ну зачем оно тебе?
Я ж тебе парадигму еще тогда объяснил, но ты опять похоже упираешься и пытаешься сделать свой самый быстрый манагер памяти.
Вот как ты думаешь - сколько лет прошло как появились компьютеры, а все никак не сделают что-то "прямо моментальное" :)
Да и нюансик есть -а задача то какая, что так жилы рвешь? :)

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

Можно конечно заоптимизировать все, но смысл, да и кто за это заплатит?


 
DevilDevil ©   (2013-10-31 23:00) [30]

> DVM ©   (31.10.13 22:56

не понимаю, за счёт чего там должен быть профит
ибо immutable, насколько я понимаю, предполагает копирование на каждый чих
а стандартный подход не предполагает


 
DevilDevil ©   (2013-10-31 23:05) [31]

> Rouse_ ©   (31.10.13 22:59) [29]

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

по поводу парадигмы. Я не упёрся. Я просто перепроверил. Ибо сказанное тобой не умещалось в моей голове

по поводу что нужно оптимизировать - очень просто. Что используется часто, обрабатывает объёмы данных. К примеру строки и (даже) менеджмент памяти стоит оптимизировать. Что собственно и делали Borland/Embarcadero (копируя FastCode)

смысл и кто за это заплатит? да никто. поэтому желание есть, а мотивации нет :)))


 
DevilDevil ©   (2013-10-31 23:06) [32]

ну единственная мотивация может быть если процесс и/или результат будет интересен другим людям. а так чисто для меня - скорее всего не имеет смысла :)


 
DVM ©   (2013-10-31 23:09) [33]


> DevilDevil ©   (31.10.13 23:00) [30]


> не понимаю, за счёт чего там должен быть профит

Памяти меньше расходуется, и сравнение происходит быстрее, так как требует сравнение лишь ссылок.


 
DevilDevil ©   (2013-10-31 23:16) [34]

> DVM ©   (31.10.13 23:09) [33]

што ?
S1 := IntToStr(5);
S2 := IntToStr(5);
if (S1 <> S2) then ...


т.е. в данном случае указатели S1 и S2 равны ?
это будет аларм!
надеюсь, они проведут тестирование производительности прежде, чем пускать это в массы


 
DVM ©   (2013-10-31 23:22) [35]

Не равны. Immutable строки так не сравнивают .


 
Rouse_ ©   (2013-10-31 23:25) [36]


> DevilDevil ©   (31.10.13 23:05) [31]
> по поводу парадигмы. Я не упёрся. Я просто перепроверил.
>  Ибо сказанное тобой не умещалось в моей голове

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


 
DVM ©   (2013-10-31 23:26) [37]

Если две строки указывают на одно и то же место в памяти то они равны, если нет, то сравнение выполняется побайтно


 
DVM ©   (2013-10-31 23:49) [38]

Кстати, в том же с# есть такая штука как интернирование строк, так вот там две строки s1 = "5" и s2 ="5" указывают на одну и ту же область памяти даже если объявлены в разных местах программы.


 
NoUser ©   (2013-11-01 00:01) [39]

Я что-то не понял, или я не понял  - зачем так много текста то.

> [0]

var
S: string;            //  ThreadString!!!  А ДЛЯ ЧЕГО ТАКОЕ НАДО ???
ThreadCounter: integer=0;
--
InterlockedIncrement(ThreadCounter);  //  молодец ! не просто ThreadCounter:=ThreadCounter+1;
--
S := IntToStr(i);  // АХТУНГ !!!


нужно ж было поискать какой-нить Interlocked_Set_String()
так как S: string; это непростой, но Pointer,
а все  > Interlocked команды в подсчёте ссылок
работают уже с Данными по этому Pointer"у

PS
В XE4 порадовали AtomicIncrement и прочие AtomicXXX функции.


 
Inovet ©   (2013-11-01 00:02) [40]

> [38] DVM ©   (31.10.13 23:49)

Ну такое можно и просто в Си++ компиляторах директивами компиляции включить, только поплохеть иногда может.


 
DevilDevil ©   (2013-11-01 00:15) [41]

> Rouse_ ©   (31.10.13 23:25) [36]

Розыч, ты говоришь так, будто никогда не ошибаешься
возьмём сегодняшний xchg лол )))

> DVM ©   (31.10.13 23:26) [37]
> Если две строки указывают на одно и то же место в памяти
> то они равны, если нет, то сравнение выполняется побайтно


ну дак по идее и сейчас никто не мешает сравнивать
более того, при сравнении на равенство строк, эффективно предварительно сравнить длины, а не вызывать каждый раз тяжёлую Compare функцию :)

> NoUser ©   (01.11.13 00:01) [39]
> В XE4 порадовали AtomicIncrement и прочие AtomicXXX функции.


если не сложно - приведи, чё там в недрах
а то XE4 не ма :)


 
NoUser ©   (2013-11-01 00:42) [42]

В недрах там просто system.pas, так как попадают в разряд "Delphi Intrinsic Routines" (магия компилятора короче)

Озвучены (в справке) например так:

function AtomicExchange(var Target; Value: <Integer or NativeInt or Pointer>): Integer; overload;
function AtomicExchange(var Target; Value: <Integer or NativeInt or Pointer>): Int64; overload;
function AtomicExchange(var Target; Value: <Integer or NativeInt or Pointer>): Pointer; overload;
function AtomicExchange(var Target; Value: <Integer or NativeInt or Pointer>): NativeInt; overload
;

Я как-то пытался заинлайнить самописные, тут вроде спрашивал что-то по асму, и для < XE4 пользую свои Interlocked_xxx (могу выслать)


 
DevilDevil ©   (2013-11-01 00:43) [43]

> NoUser ©   (01.11.13 00:42) [42]

ясно


 
Пит   (2013-11-01 01:41) [44]

тема интересная.

Конечно, строки не потокобезопасные. Чтобы они стали таковыми - надо при обращении к ним использовать объекты синхронизации, чего по факту по-умолчанию не происходит.
Почему "обычные" строки не потокобезопасные - понятно, зачем расходовать ресурсы на в общем далеко не всем нужную вещь.

Почему нету потокобезопасных строк совместно с потокоопасными - фиг знает, может быть когда и будут.

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

constructor TMyScannerThread.Create(AHost: string); ...
begin
  FHost := AHost;
...


Соответственно, в Execute поток берет из своего private поля значение FHost, присваивает его соответствующему полю компоненту Indy динамически созданному и делает коннект. Оценить насколько здесь важна роль потокобезопасности строк - сложно, на мой взгляд. В более тяжелых примерах - еще сложнее.

Я помню в свое время в таких "подозрительных" моментах делал UniqueString, потом по опыту вижу, что никто и никогда этим "не заморачивается". И сам перестал параноить, по факту всегда все работало хорошо, я не выявлял багов в реальных приложениях, связанных с потокобезопасностью строк, хотя факт потоко небезопасности очевиден.

Засим у меня до сих пор непонятное отношение ко всему этому. Да - строки не потокобезопасные, но почти везде в случае наличия потоков с ними работают так, как будто они потокобезопасные. Есть ли в этом магия или что-то еще - я не знаю, но впечатления и отношение какое-то такое)


 
DevilDevil ©   (2013-11-01 01:46) [45]

> Пит   (01.11.13 01:41) [44]

разобрали вроде уже
пока я остановился в этом вопрос на формулировке [15]


 
Пит   (2013-11-01 02:26) [46]


> разобрали вроде уже

с чем разобрались? )

В [15] написана банальная вещь. Спору нет, так оно и есть, но если условия другие? Распространенное условие я описал в [44] про поток TMyScannerThread


 
Eraser ©   (2013-11-01 08:31) [47]


> DevilDevil ©   (31.10.13 16:44) [15]

я бы в таких спорных местах не использовал бы ARC объекты, а заюзал бы TBytes и самостоятельно разграничил доступ на запись и чтение крит. секциями.


 
DVM ©   (2013-11-01 10:14) [48]


> Пит   (01.11.13 01:41) [44]


> constructor TMyScannerThread.Create(AHost: string); ...
> begin
>   FHost := AHost;
> ...
>
> Соответственно, в Execute поток берет из своего private
> поля значение FHost, присваивает его соответствующему полю
> компоненту Indy динамически созданному и делает коннект.
>  Оценить насколько здесь важна роль потокобезопасности строк
> - сложно, на мой взгляд.

Именно в такой записи совсем нет никакой проблемы. Строка будет скопирована, поток будет работать с копией.


 
DevilDevil ©   (2013-11-01 10:59) [49]

> Пит   (01.11.13 02:26) [46]

да, да, как раз хотел написать, то DVM меня опередил
данная ситуация как раз подпадает под формулировку [15]
т.е. переменная FHost находится во владеющем потоке


 
Cobalt ©   (2013-11-01 11:33) [50]

> Пит   (01.11.13 01:41) [44]
Если со строками происходит постоянная безостановочная работа - тогда да, в многопоточном приложении понадобится синхронизация работы с ними.
Но не со всеми подряд, конечно, а "с избранными".
Даже если в цикле запускается пара десятков потоков, каждому из которых передается строка с адресом - все равно сначала увидят что у неё счётчик ссылок >0, сделают себе копию, а потом уже будут фигачить в неё данные.

Тут уж, на мой дилетантский взгляд, есть риск не запортить память, а "потерять её", если два потока одновременно сделают себе копию, и останется оригинал.


 
jack128_   (2013-11-01 12:00) [51]


> Соответственно, в Execute поток берет из своего private
> поля значение FHost, присваивает его соответствующему полю
> компоненту Indy динамически созданному и делает коннект.
>  Оценить насколько здесь важна роль потокобезопасности строк
> - сложно, на мой взгляд.

а что сложного, если не секрет??


> Тут уж, на мой дилетантский взгляд, есть риск не запортить
> память, а "потерять её", если два потока одновременно сделают
> себе копию, и останется оригинал.

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


 
Владислав ©   (2013-11-01 12:05) [52]

DVM ©   (01.11.13 10:14) [48]
> Строка будет скопирована, поток будет работать с копией.

С чего бы вдруг?


 
Пит   (2013-11-01 13:29) [53]


> Строка будет скопирована, поток будет работать с копией.

почему строка будет скопирована?


 
Ega23 ©   (2013-11-01 13:32) [54]

Я бы сказал, неопределённое поведение будет. С одной стороны, при изменении первой, вроде как копия получается. С другой в асме вижу IncRef


 
Sha ©   (2013-11-01 14:10) [55]

> DevilDevil ©   (31.10.13 21:42) [25]

> чёт я исходников не вижу...

поверь, они там есть


> но вот здесь вот у тебя большая ошибка в рассуждениях

укажи точное место и какая конкретно ошибка, без общих рассуждений о том, что все знают)


> твой VeryBadCompareInt и есть самый правильный способ сравнения

это где? и какой правильный?


 
DVM ©   (2013-11-01 14:17) [56]


> Владислав ©   (01.11.13 12:05) [52]
> DVM ©   (01.11.13 10:14) [48]
> > Строка будет скопирована, поток будет работать с копией.
>
>
> С чего бы вдруг?

с того, что по значению передается


 
DVM ©   (2013-11-01 14:27) [57]

Для разных типов строк кстати по разному это копирование будет проведено. Для коротких строк реально будет скопировано, для AnsiString будет вызвана LStrAddRef и реального копирования не будет, если никто строку изменять не захочет, что будет для других типов строк я не смотрел. Огрести тут проблем шанс минимальный.


 
DevilDevil ©   (2013-11-01 14:46) [58]

> Sha ©   (01.11.13 14:10) [55]

> поверь, они там есть
я верю
дай пожалуйста ссылку

OFFTOP
> укажи точное место и какая конкретно ошибка, без общих рассуждений
> о том, что все знают)

> это где? и какой правильный?

Получилось быстро, но неверно. Если при вычислении разности произойдет переполнение, то это приведет к ошибочному результату.
Получилось быстро и верно. Ошибочного результата не будет.


 
Пит   (2013-11-01 14:57) [59]


> Для коротких строк реально будет скопировано, для AnsiString
> будет вызвана LStrAddRef и реального копирования не будет

я думал очевидно, что мы ведем речь об Ansi/Wide-string. По-умолчанию, string это AnsiString или WideString, уже очень давно.

И вот так, если сделать: FHost := AHost, а потом это еще присвоить значению .Host компонента Indy - фиг его знает насчет потокобезопасности. Наверное, где-то что-то может и "рвануть", но в реальном коде никогда не видел защиты от этого.


 
DVM ©   (2013-11-01 15:02) [60]


> Пит   (01.11.13 14:57) [59]


>  Ansi/Wide-string

Ты не мешай в одну кучу все строки. Сейчас строки UnicodeString, а не WideString.
WideString вообще особая песня, тоже кстати будет скопировано, если я правильно понял код в system.pas.


 
Sha ©   (2013-11-01 15:05) [61]

> DevilDevil ©   (01.11.13 14:46) [58]
> дай пожалуйста ссылку

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


> Получилось быстро и верно. Ошибочного результата не будет.

Вот тебе пример, что это не так:
Очевидно, что MaxInt>-1.
Но разность MaxInt-(-1)=MaxInt+1=MinInt<0.
Именно поэтому VeryBadCompareInt нигде в Delphi не используется.


 
DVM ©   (2013-11-01 15:07) [62]


> И вот так, если сделать: FHost := AHost, а потом это еще
> присвоить значению .Host компонента Indy - фиг его знает
> насчет потокобезопасности. Наверное, где-то что-то может
> и "рвануть"

Вряд ли Indy меняет в процессе работы значение этого поля.
Не может оно рвануть, если со строками обращаться как со строками, а не через указатели, как с массивами и т.д. Если кто-то собирается изменить строку, на которую есть еще ссылки, то этот кто-то сначала получит ее копию, а уж потом будет менять. Компилятор такой код построит сам.


 
Smile   (2013-11-01 15:09) [63]

СТРОКИ НЕ ПОТОКОБЕЗОПАСНЫ
Отожми клавишу Caps Lock или убери локоть с клавии Shift
:(


 
DevilDevil ©   (2013-11-01 15:24) [64]

> Sha ©   (01.11.13 15:05) [61]

> прямую ссылку дать не могу, там защита от ботов
если не сложно - кинь тогда архивчик
интересно же

> Вот тебе пример, что это не так:
> Очевидно, что MaxInt>-1.
> Но разность MaxInt-(-1)=MaxInt+1=MinInt<0.
> Именно поэтому VeryBadCompareInt нигде в Delphi не используется.


Твоя правда
Публично признаю, что был не прав
оказывается для jl и jg смотрятся не только флаги знака, но и переполнения

p.s. теперь стало понятно, почему в сях принято использовать bool функцию для сортировок
благодарю за то, что ликвидировал ошибку в моих рассуждениях


 
DevilDevil ©   (2013-11-01 15:42) [65]

> Sha ©

Зацени решение без прыжков, большая часть команд выполнится в конвейере одновременно
function IntsCompare(const X, Y: integer): integer;
begin
 Result := shortint(X >= Y) - shortint(X <= Y);
end;


 
Sha ©   (2013-11-01 15:51) [66]

> DevilDevil ©   (01.11.13 15:42) [65]

нечто подобное на BASMе делал,
думаю, Самая быстрая на Земле побыстрее будет


 
DevilDevil ©   (2013-11-01 15:55) [67]

> думаю, Самая быстрая на Земле побыстрее будет

если ты про это:
function ShaCompareInt(Item1, Item2: Pointer): Integer;
var
 Second: integer;
begin;
 Result:=Integer(Item1^);
 Second:=Integer(Item2^);
 if Result xor Second>=0
   then Result:=Result-Second
   else Result:=Result or 1;
 end;
end;


То здесь прыжки. А вообще да, нужно организовывать сортировки с минимумом вызываемых функций. PInteger(Item1)^ < PInteger(Item2)^ можно и инлайном делать


 
Sha ©   (2013-11-01 15:58) [68]

> DevilDevil ©   (01.11.13 15:55) [67]
> То здесь прыжки.

И че?


 
DevilDevil ©   (2013-11-01 16:11) [69]

> И че?

эмм
ну не так страшен прыжок, как ошибка его предсказания (длинна конвейера. ~12 тактов)
да и сам прыжок тоже весит


 
Sha ©   (2013-11-01 16:14) [70]

> DevilDevil ©   (01.11.13 16:11) [69]

ты не умничай, а просто проверь )


 
DevilDevil ©   (2013-11-01 16:19) [71]

А вот так кстати вообще ништяк:
function IntsCompare(const X, Y: integer): integer;
begin
 Result := shortint(shortint(X >= Y) - shortint(X <= Y));
end;


получается:
cmp edx, eax
setle cl
cmp edx, eax
setnl al
sub cl, al
movsx eax, cl

 
Я насчитал 4 такта на всё


 
Sha ©   (2013-11-01 16:27) [72]

что-то в этом духе я пробовал, осталось проверить, вдруг звезды сегодня стоят иначе )


 
DevilDevil ©   (2013-11-01 16:57) [73]

function DD_CompareInt(Item1, Item2: Pointer): Integer;
var
 Second: integer;
begin;
 Result := Integer(Item1^);
 Second := Integer(Item2^);
 Result := shortint(shortint(Result >= Second) - shortint(Result <= Second));
end;

function ShaCompareInt(Item1, Item2: Pointer): Integer;
var
Second: integer;
begin;
Result:=Integer(Item1^);
Second:=Integer(Item2^);
if Result xor Second>=0
  then Result:=Result-Second
  else Result:=Result or 1;
end;

const
 ITERATIONS_COUNT = 10000000;
 ARR: array[0..4] of integer = (low(integer), -1, 0, 1, high(integer));

procedure TestDD_();
var
 T: dword;
 i: integer;
begin
 T := GetTickCount;
 for i := 1 to ITERATIONS_COUNT  do
 begin
   DD_CompareInt(@ARR[0], @ARR[0]);
   DD_CompareInt(@ARR[0], @ARR[1]);
   DD_CompareInt(@ARR[0], @ARR[2]);
   DD_CompareInt(@ARR[0], @ARR[3]);
   DD_CompareInt(@ARR[0], @ARR[4]);
   DD_CompareInt(@ARR[1], @ARR[0]);
   DD_CompareInt(@ARR[1], @ARR[1]);
   DD_CompareInt(@ARR[1], @ARR[2]);
   DD_CompareInt(@ARR[1], @ARR[3]);
   DD_CompareInt(@ARR[1], @ARR[4]);
   DD_CompareInt(@ARR[2], @ARR[0]);
   DD_CompareInt(@ARR[2], @ARR[1]);
   DD_CompareInt(@ARR[2], @ARR[2]);
   DD_CompareInt(@ARR[2], @ARR[3]);
   DD_CompareInt(@ARR[2], @ARR[4]);
   DD_CompareInt(@ARR[3], @ARR[0]);
   DD_CompareInt(@ARR[3], @ARR[1]);
   DD_CompareInt(@ARR[3], @ARR[2]);
   DD_CompareInt(@ARR[3], @ARR[3]);
   DD_CompareInt(@ARR[3], @ARR[4]);
   DD_CompareInt(@ARR[4], @ARR[0]);
   DD_CompareInt(@ARR[4], @ARR[1]);
   DD_CompareInt(@ARR[4], @ARR[2]);
   DD_CompareInt(@ARR[4], @ARR[3]);
   DD_CompareInt(@ARR[4], @ARR[4]);
 end;
 T := GetTickCount-T;
 Writeln("TestDD_ = ", T, "ms");
end;

procedure TestSha();
var
 T: dword;
 i: integer;
begin
 T := GetTickCount;
 for i := 1 to ITERATIONS_COUNT  do
 begin
   ShaCompareInt(@ARR[0], @ARR[0]);
   ShaCompareInt(@ARR[0], @ARR[1]);
   ShaCompareInt(@ARR[0], @ARR[2]);
   ShaCompareInt(@ARR[0], @ARR[3]);
   ShaCompareInt(@ARR[0], @ARR[4]);
   ShaCompareInt(@ARR[1], @ARR[0]);
   ShaCompareInt(@ARR[1], @ARR[1]);
   ShaCompareInt(@ARR[1], @ARR[2]);
   ShaCompareInt(@ARR[1], @ARR[3]);
   ShaCompareInt(@ARR[1], @ARR[4]);
   ShaCompareInt(@ARR[2], @ARR[0]);
   ShaCompareInt(@ARR[2], @ARR[1]);
   ShaCompareInt(@ARR[2], @ARR[2]);
   ShaCompareInt(@ARR[2], @ARR[3]);
   ShaCompareInt(@ARR[2], @ARR[4]);
   ShaCompareInt(@ARR[3], @ARR[0]);
   ShaCompareInt(@ARR[3], @ARR[1]);
   ShaCompareInt(@ARR[3], @ARR[2]);
   ShaCompareInt(@ARR[3], @ARR[3]);
   ShaCompareInt(@ARR[3], @ARR[4]);
   ShaCompareInt(@ARR[4], @ARR[0]);
   ShaCompareInt(@ARR[4], @ARR[1]);
   ShaCompareInt(@ARR[4], @ARR[2]);
   ShaCompareInt(@ARR[4], @ARR[3]);
   ShaCompareInt(@ARR[4], @ARR[4]);
 end;
 T := GetTickCount-T;
 Writeln("TestSha = ", T, "ms");
end;

begin
 TestDD_();
 TestSha();
 
 Readln;
end.


Результат:
 TestDD_ = 420ms-437ms
 TestSha = 499ms

Но может на данных приближённых к реальным будет быстрее :)


 
Хаус   (2013-11-01 17:17) [74]


> DevilDevil ©   (01.11.13 16:57) [73]


Надо еще приоритет потоку поставить высокий,
при таких вычислениях.


 
Sha ©   (2013-11-01 17:48) [75]

Идея Самой быстрой заключается в том,
что на реальных данных переполнения случаются редко,
и, значит предсказание переходов почти 100%-ное.

Играющая с флагами - то, что было медленно - упрощенная DD_CompareInt
Самая басматая - упрощенная Самая быстрая

У меня на тесте без переполнений результат, естественно другой:  


//Самая быстрая
function IntCmp1(px, py: pinteger): integer;
var
 t: integer;
begin;
 Result:=px^;
 t:=py^;
 if Result xor t>=0
   then Result:=Result-t
   else Result:=Result or 1;
 end;

//Играющая с флагами
function IntCmp2(px, py: pinteger): integer;
asm
 mov eax, dword ptr [eax]
 mov edx, dword ptr [edx]
 cmp eax, edx
 setle cl
 setge al
 sub al, cl
 movsx eax, al
 end;

//Самая басматая
function IntCmp3(px, py: pinteger): integer;
asm
 mov eax, dword ptr [eax]
 mov edx, dword ptr [edx]
 sub eax, edx
 jo @over
 ret
@over:
 add eax, edx
 or eax, 1
 end;

procedure TForm1.Button2Click(Sender: TObject);
var
 a: array[0..99] of integer;
 t1, t2, t3, i, j: integer;
begin;
 RandSeed:=2154376353;
 for i:=0 to High(a) do a[i]:=Random(1000000);

 t1:=GetTickCount;
 for j:=0 to 9999999 do for i:=1 to High(a) do begin;
   IntCmp1(@a[i],@a[i-1]);
   IntCmp1(@a[i-1],@a[i]);
   end;
 t1:=GetTickCount-t1;

 t2:=GetTickCount;
 for j:=0 to 9999999 do for i:=1 to High(a) do begin;
   IntCmp2(@a[i],@a[i-1]);
   IntCmp2(@a[i-1],@a[i]);
   end;
 t2:=GetTickCount-t2;

 t3:=GetTickCount;
 for j:=0 to 9999999 do for i:=1 to High(a) do begin;
   IntCmp3(@a[i],@a[i-1]);
   IntCmp3(@a[i-1],@a[i]);
   end;
 t3:=GetTickCount-t3;

 Memo1.Lines.Add(IntToStr(t1)); //3104 ms
 Memo1.Lines.Add(IntToStr(t2)); //3635 ms
 Memo1.Lines.Add(IntToStr(t3)); //3089 ms
 end;



 
Пит   (2013-11-01 20:36) [76]


> Ты не мешай в одну кучу все строки. Сейчас строки UnicodeString,
>  а не WideString.

да? а чем отличается от WideString?

Я сейчас разговариваю на уровне Delphi 7, там никаких UnicodeString нету..


 
DevilDevil ©   (2013-11-01 21:27) [77]

> Sha ©   (01.11.13 17:48) [75]

ясно.


 
DVM ©   (2013-11-01 21:44) [78]


> Пит   (01.11.13 20:36) [76]


> да? а чем отличается от WideString?

Отличается что? Обычный string, UnicodeString? Управление памятью WideString WideString осуществляется системным менеджером памяти (очень удобно в ряде случаев), обычного string - дельфийским.


 
DVM ©   (2013-11-01 22:07) [79]


> Пит   (01.11.13 20:36) [76]

Подсчета ссылок в WideString кстати нет вообще, поэтому они и будут копироваться.


 
Пит   (2013-11-01 22:19) [80]


> Отличается что?

WideString от UnicodeString


> Подсчета ссылок в WideString кстати нет вообще

да?! Вот это новость... Я был уверен, что WideString - полная копия AnsiString, только символ двухбайтный.


 
DVM ©   (2013-11-01 22:23) [81]


> Пит   (01.11.13 22:19) [80]


> да?! Вот это новость... Я был уверен, что WideString - полная
> копия AnsiString, только символ двухбайтный.

не, это не копия, и это очень хорошо, особенно хорошо такие строки передавать из в длл. Менеджер памяти то один получается и в длл и в программе.

вот тут один человек пытается добавить в WideString строки подсчет ссылок и дельфийский менеджер памяти: http://dign.narod.ru/articles/fastwidestring/


 
Владислав ©   (2013-11-02 02:24) [82]

DVM ©   (01.11.13 14:17) [56]

> Владислав ©   (01.11.13 12:05) [52]
> DVM ©   (01.11.13 10:14) [48]
> > Строка будет скопирована, поток будет работать с копией.
>
>
> С чего бы вдруг?

> с того, что по значению передается

Жуть какая. Не уж то открытие?.. Ну с новыми знаниями!


 
DVM ©   (2013-11-02 10:34) [83]


> Владислав ©   (02.11.13 02:24) [82]

> Жуть какая.


Что вас так удивило? Что передается по значению? Да, именно по значению, а не по ссылке, хоть string и является ссылочным типом данных.

Владислав, прочитайте всю ветку, ряд типов строк (ShortString, WideString) будет скопированы немедленно при передаче функцию таким образом (см асм код и модуль system). Те, что не будут скопированы при передаче немедленно, все равно будут скопированы по месту, где их попытаются изменить (посмотрите асм код), если их будут менять как строки (!) (например, так s := s + "a" или так s := "a" или так SetLength(a, 10) и т.д.), а не как массивы, обращаясь к отдельным символам или по указателю на них как области памяти.

Примитивный пример:

procedure Test(s: string);
var
 a: string;
begin
 a  := s;
 a := a + "5";
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 Test(s);
end;


Его ASM вариант такой:

Unit1.pas.29: a  := s;
0051130C 8D45F8           lea eax,[ebp-$08]
0051130F 8B55FC           mov edx,[ebp-$04]
00511312 E8795CEFFF       call @UStrLAsg
Unit1.pas.30: a := a + "5";
00511317 8D45F8           lea eax,[ebp-$08]
0051131A BA58135100       mov edx,$00511358
0051131F E87865EFFF       call @UStrCat


UStrCat находится в System и копирует содержимое строки при необходимости. Тут нет никакой проблемы при многопоточном доступе, изменяться всегда будут разные строки.

Согласен, некоторая ненулевая вероятность того, что компилятор построит код, в котором копирования не произойдет перед изменением имеется, но такие ситуацие редки.


 
DVM ©   (2013-11-02 10:45) [84]

Отсюда вытекает [10]. Если на одну и ту же строку имеется несколько ссылок, то по разным ссылкам можно смело обращаться к строке из нескольких потоков, т.к подсчет ссылок сам по себе атомарен, а перед любым изменением строки на которую есть еще ссылки гарантированно произойдет копирование этой строки. При условии, что мы к строкам обращаемся как именно к строкам, а не массивам или указателям.


 
Владислав ©   (2013-11-02 13:31) [85]

DVM ©   (02.11.13 10:34) [83]

Не пишите ерунду. При передаче строки в качестве параметра строка не копируется. А модификатор const или его отсутствие влияют на изменение счетчика ссылок.
И к чему столько рассуждений и примеров на тему присвоения строке нового значения?
Всю тему перечитывать не нужно. Перечитайте 48 и 56. Можете даже написать 48 и заглянуть в asm view.


 
DVM ©   (2013-11-02 14:20) [86]


> Владислав ©   (02.11.13 13:31) [85]


> Не пишите ерунду. При передаче строки в качестве параметра
> строка не копируется.

Где ерунда. Зависит от строки. WideString скопируется. ShortString скопируется.


procedure Test(s: WideString);
var
 a: WideString;
begin
 a := s;
end;


в CPU View это выглядлит как:


Unit1.pas.29: a := s;
00511314 8D45F8           lea eax,[ebp-$08]
00511317 8B55FC           mov edx,[ebp-$04]
0051131A E8C55CEFFF       call @WStrLAsg


WStrLAsg приведет нас под Windows в результате к SysReAllocStringLen, которая создает новую строку BSTR  и копирует данные из старой, как следует из MSDN.

Аналогично для ShortString, только там будет вызвана PStrCpy.

Непосредственно только при передаче строка AnsiString и UnicodeString не копируется конечно, своей фразой я не это хотел сказать. Она скопируется позже при попытке ее изменить при наличии еще ссылок на нее.


> А модификатор const или его отсутствие влияют на изменение
> счетчика ссылок.

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

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


> Можете даже написать 48 и заглянуть в asm view.

Да глядел я. Мы о разном говорим.


 
DevilDevil ©   (2013-11-02 20:13) [87]

Удалено модератором
Примечание: Личная пере(бран)писка


 
картман ©   (2013-11-02 21:12) [88]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Так, закрываемся!   (2013-11-02 23:47) [89]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 01:47) [90]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 01:49) [91]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Sapersky   (2013-11-03 02:46) [92]

Удалено модератором
Примечание: Личная пере(бран)писка


 
DevilDevil ©   (2013-11-03 03:41) [93]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 03:43) [94]

Удалено модератором
Примечание: Личная пере(бран)писка


 
DevilDevil ©   (2013-11-03 03:44) [95]

Удалено модератором
Примечание: Личная пере(бран)писка


 
DevilDevil ©   (2013-11-03 03:45) [96]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 03:49) [97]

DevilDevil ©   (03.11.13 03:41) [93]

Буквоед. Есть такое. Я в [94] объяснил, почему.

Вот ты пишешь:
constructor TMyClass.Create(const AHost: string);
begin
 FHost := AHost; // брейк поинт здесь
end;


А сам то брекпоинт ставил? И что, реально копирование происходит?.. А если проследовать во все вызываемые процедуры?..


 
DevilDevil ©   (2013-11-03 03:50) [98]

Удалено модератором
Примечание: Бан неизбежен как кризис империализма


 
DevilDevil ©   (2013-11-03 03:59) [99]

и да, случай чтения или копирования константной строки - идеальный с точки зрения потокобезопасности


 
Владислав ©   (2013-11-03 04:01) [100]

DevilDevil ©   (03.11.13 03:50) [98]

Ткни, конечно. Я, как и все остальные, тоже могу ошибаться.

DevilDevil ©   (03.11.13 03:59) [99]

Сразу извини за манеру подачи. Не пиши ерунды.


 
DevilDevil ©   (2013-11-03 04:08) [101]

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

у меня Delphi7, у меня код идёт в _LStrAsg
там он попадает на CALL _NewAnsiString и CALL Move

Если у тебя Unicode-версия Delphi, то придти ты должен в _UStrAsg
может быть ещё в _UStrAddRef но там он проработает вхолостую


 
Владислав ©   (2013-11-03 04:12) [102]

DevilDevil ©   (03.11.13 04:08) [101]

> у меня Delphi7, у меня код идёт в _LStrAsg
> там он попадает на CALL _NewAnsiString и CALL Move

Можно пример вместе с кодом на паскале? Уж ткнуть носом, так ткнуть.


 
DevilDevil ©   (2013-11-03 04:16) [103]

> Владислав ©   (03.11.13 04:12) [102]

а ты не знаешь как в System в дебаге зайти что ли ?

procedure _LStrAsg(var dest; const source);
{$IFDEF PUREPASCAL}
var
 S, D: Pointer;
 P: PStrRec;
 Temp: Longint;
begin
 S := Pointer(source);
 if S <> nil then
 begin
   P := PStrRec(Integer(S) - sizeof(StrRec));
   if P.refCnt < 0 then   // make copy of string literal
   begin
     Temp := P.length;
     S := _NewAnsiString(Temp);
     Move(Pointer(source)^, S^, Temp);
     P := PStrRec(Integer(S) - sizeof(StrRec));
   end;
   InterlockedIncrement(P.refCnt);
 end;

 D := Pointer(dest);
 Pointer(dest) := S;
 if D <> nil then
 begin
   P := PStrRec(Integer(D) - sizeof(StrRec));
   if P.refCnt > 0 then
     if InterlockedDecrement(P.refCnt) = 0 then
       FreeMem(P);
 end;
end;
{$ELSE}
asm
       { ->    EAX pointer to dest   str      }
       { ->    EDX pointer to source str      }

               TEST    EDX,EDX                           { have a source? }
               JE      @@2                               { no -> jump     }

               MOV     ECX,[EDX-skew].StrRec.refCnt
               INC     ECX
               JG      @@1                               { literal string -> jump not taken }

               PUSH    EAX
               PUSH    EDX
               MOV     EAX,[EDX-skew].StrRec.length
               CALL    _NewAnsiString
               MOV     EDX,EAX
               POP     EAX
               PUSH    EDX
               MOV     ECX,[EAX-skew].StrRec.length
               CALL    Move
               POP     EDX
               POP     EAX
               JMP     @@2

@@1:
          LOCK INC     [EDX-skew].StrRec.refCnt

@@2:            XCHG    EDX,[EAX]
               TEST    EDX,EDX
               JE      @@3
               MOV     ECX,[EDX-skew].StrRec.refCnt
               DEC     ECX
               JL      @@3
          LOCK DEC     [EDX-skew].StrRec.refCnt
               JNE     @@3
               LEA     EAX,[EDX-skew].StrRec.refCnt
               CALL    _FreeMem
@@3:
end;
{$ENDIF}


 
Владислав ©   (2013-11-03 04:26) [104]

DevilDevil ©   (03.11.13 04:16) [103]

Ты удалаешься от [48].
Ну уж сказал А, говори Б.
Уже ткни меня носом, в каком месте копирование строки?


 
DevilDevil ©   (2013-11-03 04:33) [105]

> Владислав ©   (03.11.13 04:26) [104]

я же тебе буквами по форуму написал
1) тестируешь написанный мной в [93] код
2) останавливаешься в брейкпоинте на указанное место
3) трейсишь в недра
4) наблюдаешь NewAnsiString и Move

что не ясно ?


 
Владислав ©   (2013-11-03 04:43) [106]

DevilDevil ©   (03.11.13 04:33) [105]

Ты меня немного утомил. Ты не мой ученик, а я не твой учитель, чтобы тебе "жевать и в рот складывать".
Воспользуйся своим алгоритмом, и уразумей таки.
Подсказка тебе: ты точно понимаешь смысл вот этой строки в приведенном тобой коде:
if P.refCnt < 0 then   // make copy of string literal
?
Там даже комментарий есть. Ты и вправду не вникаешь?


 
DevilDevil ©   (2013-11-03 04:47) [107]

Тяжёлый случай :)
Ладно, я спать :)


 
Sapersky   (2013-11-03 06:44) [108]

Константы, сэр...

И кстати, я сформулировал принцип "частичной потокобезопасности" строк.
Это вообще не потокобезопасность.
Это обеспечение элементарной логики, согласно которой РАЗНЫЕ переменные, используемые в разных потоках, не должны друг от друга зависеть.
В случае строк разные переменные могут фактически указывать на одну и ту же строку, и RTL пытается по возможности это скрыть. Работает это почти всегда, за исключением случаев, когда разработчик, злобный буратина, пишет в "неявно общую" строку через PChar (при обращениях вроде s[n] должна вызываться UniqueString).

В принципе, DVM с самого начала говорил что-то в этом роде - "c чего ты взял, что строки потокобезопасны" и т.д.


 
DVM ©   (2013-11-03 11:02) [109]


> Владислав


> Для того, чтобы "огрести" по полной с потоками и верой,
> что строки копируются, достаточно написать тестовый пример,
>  используя совершенно стандартное приведение типов:
> Изменение строки:
> PChar(StringVariable)^ := "B"

Так разумеется нельзя, я об этом писал [62,83,84], проблемы могут быть. Никаких указателей. Такой случай в данном контексте не рассматривается.


> Владислав ©   (03.11.13 03:43) [94]


>  Кроме всего прочего, само копирование не потокобезопасно.
>  

Само по себе конечно не безопасно, я писал там выше про UniqueString [11]. Но тут дело в том, что в нашем случае ВСЕ вызовы изменения строки будут обернуты таким кодом, что перед изменением всегда будет создаваться копия. А это означает, что оригинал никто не будет править, все будут править свои копии. Ведь так? Но при соблюдении определенных условий (без указателей и проч.). Приведенный Пит-ом пример как раз идеальный для иллюстрации этого.
Это при разумной организации логики программы конечно. Ситуаций когда один поток производит изменение строки, а в это время другой поток получает ссылку на ту же строку и начинает создавать себе копию не должно получаться, если строки в потоки передают как описано выше, а не по указателям.


> Sapersky   (03.11.13 06:44) [108]


> И кстати, я сформулировал принцип "частичной потокобезопасности"
> строк.

Ну в общем случае, да, получается так.


> при обращениях вроде s[n] должна вызываться UniqueString

Я посмотрел, менять отдельные символы безопасно, тоже создается копия при необходимости, так что из [62,83,84] массив можно вычеркнуть. Т.о опасным остается только приведение к указателям и изменение по указателям.


 
DVM ©   (2013-11-03 13:06) [110]


> Владислав ©   (03.11.13 04:43) [106]


> Подсказка тебе: ты точно понимаешь смысл вот этой строки
> в приведенном тобой коде:
> if P.refCnt < 0 then   // make copy of string literal
> ?
> Там даже комментарий есть. Ты и вправду не вникаешь?

Я вот сейчас проверил в D7, все именно так, как описывает DevilDevil, есть разница по всей видимости между присвоением полю класса, через метод класса и просто переменной внутри обычной функции.

Тестовый код такой:

TMyClass = class
 private
   FFileld: string;
 public
   constructor Create(const S: string);
 end;

{ TMyClass }

constructor TMyClass.Create(const S: string);
begin
 FFileld := S;
end;


Вызываем так:

procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 TMyClass.Create(S);
end;


Точку останова ставим на   FFileld := S; включаем UseDebugDCU запускаем по F7 входим в _LStrAsg, там попадаем на

CALL    _NewAnsiString

Которая создает новую строку, а затем Move копирует данные.

----------------------------------------------------------------

Пример 2:



procedure Test(const s: AnsiString);
var
 a: AnsiString;
begin
 a := s;
end;


Вызываем:

procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 Test(s);
end;


Попадаем в _LStrLAsg, в ней нет копирования, там только счетчик ссылок меняется.


 
Владислав ©   (2013-11-03 19:15) [111]

DVM ©   (03.11.13 13:06) [110]

Не передавайте туда литерал, передайте нечто подобное: StringOfChar("1", 10).

А то Вы мне доказываете, что при присвоении литералы копируются. Так это понятно, _LStrLAsg даже соответствующие комментарии имеет, но речь то не об этом.
Вы разобрались, почему у Вас в примере 1 срока скопировалась, а в примере 2 нет?


 
DVM ©   (2013-11-03 22:26) [112]


> Владислав ©   (03.11.13 19:15) [111]


> Вы разобрались, почему у Вас в примере 1 срока скопировалась,
>  а в примере 2 нет?

Очевидно, что дело в области видимости переменных, которым присваивается значение. Для локальных вызываеся функция _LStrLAsg, у которой L в названиии и обозначает Local, для остальных _LStrAsg.

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

Если же переменная имеет шанс использоваться и в дальнейшем после присваивания (глобальная переменная или поле класса), то в нее копируется значение. Это более надежно и безопасно, особенно если литерал живет в DLL или пакете, который может быть выгружен и в поле класса или глобальной переменной останется ссылка в никуда.


 
Владислав ©   (2013-11-03 22:41) [113]

Кошмар...


 
DVM ©   (2013-11-03 22:52) [114]


> Владислав ©   (03.11.13 22:41) [113]

Есть другое объяснение? Просим...


 
Владислав ©   (2013-11-03 23:27) [115]

Там все же написано.
Комментарии к функции _LStrAsg из System.pas:
{ 99.03.11
 This function is used when assigning to global variables.

 Literals are copied to prevent a situation where a dynamically
 allocated DLL or package assigns a literal to a variable and then
 is unloaded -- thereby causing the string memory (in the code
 segment of the DLL) to be removed -- and therefore leaving the
 global variable pointing to invalid memory.
}:
В LStrAsg копия создается только для литералов. Зачем это сделано, написано в комментарии.


 
DVM ©   (2013-11-03 23:33) [116]


> Владислав ©   (03.11.13 23:27) [115]


> Там все же написано.

Я это видел. И что? В  [110] у меня и в первом варианте исходная строка литерал и во втором литерал, однако в первом копируется (_LStrAsg) во втором не копируется, а лишь увеличивает счетчик ссылок (_LStrLAsg). Вас ведь это интересовало? А разница именно в том, что в первом варианте строка приемник - поле класса, а во втором локальная переменная.


 
DVM ©   (2013-11-03 23:35) [117]


> Владислав ©   (03.11.13 23:27) [115]

И потрудитесь ответить в чем состоит смысл вашего:

> Владислав ©   (03.11.13 22:41) [113]
> Кошмар...

Если я вам и собственно процитировал кусок из комментария к исходнику с некоторыми дополнениями относительно локальных переменных.


 
Владислав ©   (2013-11-03 23:44) [118]

Напомню, что меня интересовало.
При передаче строки в качестве фактического параметра копия строки не создается.

Вы с этим не соглашаетесь и в доказательство своей точки зрения приводите код, в котором копия создается потому, что вы передаете литерал. Чувствуете разницу? Копия создается не потому, что Вы передали строку в качестве параметра, а потому, что Вы присваиваете переменной строку, которая указывает на литерал.
Измените одну строчку кода в Вашем примере 1 из [110]. Вместо:
procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 TMyClass.Create(S);
end;

Напишите, например, такой вариант:
procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := StringOfChar("1", 10);
 TMyClass.Create(S);
end;

И копия строки уже не будет создаваться.


 
DVM ©   (2013-11-04 00:07) [119]


> Владислав ©   (03.11.13 23:44) [118]
> Напомню, что меня интересовало.
> При передаче строки в качестве фактического параметра копия
> строки не создается.

А я не утверждал обратное. С чего вы это взяли? Я всю дорогу утверждал, что строка скопируется, если ее попробуют изменить, т.е поток будет работать с копией строки (с этого весь сыр бор начался). Правда мне сразу бы следовало расписать всю цепочку умозаключений почему скопируется, а то выходит, что скопируется исключительно из за передачи в качестве параметра. Тут я конечно фигню написал.


> Вы с этим не соглашаетесь и в доказательство своей точки
> зрения приводите код, в котором копия создается потому,
> что вы передаете литерал. Чувствуете разницу? Копия создается
> не потому, что Вы передали строку в качестве параметра,
> а потому, что Вы присваиваете переменной строку, которая
> указывает на литерал.

Это не в доказательство того, что строка копируется, а как доказательство слов DevilDevil, что в данном случае (с литералом) все действительно копируется. Да там и в исходнике написано это, сложно не заметить. Я лишь подтвердил то, что все что он говорит правда.


> И копия строки уже не будет создаваться.

Все верно, не будет.

И все же. Вот эта фраза:

> Вы разобрались, почему у Вас в примере 1 срока скопировалась,
>  а в примере 2 нет?

К чему относилась???
Очевидно, что к моему [110]. И ответом на этот вопрос будет именно то, что я написал в [112] про локальные и глобальные переменные, а совсем не то, что написано в комментарии к коду, который вы привели в [115] - это лишь объяснение того, почему литералы копируются в глобальные переменные, но про локальные там нет ни слова, а в локальные они не копируются.
А вы зачем то написали в ответ Кошмар.


 
Компромисс1 ©   (2013-11-04 18:21) [120]

Кошмар. Senior Delphi developers спорят об основаx работы со string.

String, StringBuilder, StringBuffer и Java вообще rule ;)


 
NoUser ©   (2013-11-04 22:50) [121]

Компромисс -- не для нас!

[ String, StringBuilder, StringBuffer ] и Java ==>> (SetLength + Move) и тчк.


 
Kerk ©   (2013-11-06 12:21) [122]

Вот тут интересно про скорость многопоточной работы со строкам.
http://www.delphitools.info/2013/11/05/multi-threaded-string-building-in-delphi/

Вообще, это там не первая заметка про скорость работы со строками. Небезынтересно.



Страницы: 1 2 3 4 вся ветка

Форум: "Прочее";
Текущий архив: 2014.05.04;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.86 MB
Время: 0.005 c
4-1268893097
DremLIN.Ru
2010-03-18 09:18
2014.05.04
Как достоверно определить что программа запущена из планировщика?


4-1268997623
Alex_C
2010-03-19 14:20
2014.05.04
Требование админ прав на 64-битной XP


15-1383253856
Пит
2013-11-01 01:10
2014.05.04
Мышка слишком быстрая


15-1383622291
Demo
2013-11-05 07:31
2014.05.04
Oracle


15-1383748436
SergP
2013-11-06 18:33
2014.05.04
Динамический массив записей со строками. подскажите





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский