Размер стека для потока

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Re: Размер стека для потока

Сообщение Sergei I. Gorelkin » 25.04.2014 06:23:51

Между процессорами Intel и AMD нет разницы в расходе стека, по крайней мере если запускать готовую программу (если компилировать на каждой из машин, то может появиться разница из-за настроек оптимизации). Теоретически же для Win32 минимум 4 байта, для Win64 - 16 байт, если вызываемая процедура сама ничего не вызывает и 48 байт - если вызывает. А для процессоров SPARC минимум будет 96 байт, если я не ошибаюсь.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Размер стека для потока

Сообщение VKB » 25.04.2014 09:44:37

hinst писал(а):@VKB: я оказывается немного не то проверял.
Если создавать поток функцией Windows.CreateThread и передать размер стека 0, то он выделит 16 мегабайт. То есть, можно считать, что 16 мегабайт это размер стека для потока, который выделяет операционная система по умолчанию

У API-шной функции CreateThread есть своё правило "по умолчанию", FPC на него не влияет. http://msdn.microsoft.com/en-us/library ... 85%29.aspx:
Код: Выделить всё
dwStackSize [in]

    The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is zero, the new thread uses the default size for the executable. For more information, see Thread Stack Size.

Это же правило работает и для fpc-шного BeginThread, если ему явно задать размер стека 0. Тогда стек выделится с таким же размером, какой был задан для основного потока. А он может быть и 16Мб, например. Я же пишу немного о другом. Функция BeginThread в FPC перегружена, и все её версии, которые не содержат параметра StackSize (в том числе и та, которая вызывается из конструктора TThread), в итоге вызывают "основную" версию и передают ей в качестве StackSize не 0, а константу DefaultStackSize, которая равна 4*1024*1024 (эта константа в принципе может отличаться в разных версиях FPC - я не проверял).

Что же касается остального, то Sergei I. Gorelkin уже объяснил. Затраты стека могут зависеть от разрядности приложения, наличия вложенных вызовов и настроек оптимизации.
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

Re: Размер стека для потока

Сообщение hinst » 08.05.2014 17:51:08

wavebvg писал(а):...
2. Один факт того, что при завершении потока стоит Sleep в 100мс дает понять всю серьёзность проблемы

Что-то я не найду, где там Sleep(100)
Вот сейчас специально смотрел и не нашёл
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Размер стека для потока

Сообщение wavebvg » 10.05.2014 13:34:33

hinst писал(а):Что-то я не найду, где там Sleep(100)
Вот сейчас специально смотрел и не нашёл

В реализаций "unix"
Для примера, попробуйте создать 100 потоков, запустить их и потом разом их завершить. Просто меня это очень сильно поразило в исходниках (на линуксе прекрасно работает, даже если убрать эту задержку)
Просто делал кое что с потоками в целях собственного просвящения link.
Когда приложение завершается и нужно остановить большой пул - немыслимые задержки. Думал сам накосячил. Через неделю начал копать нашел в исходниках fpc кусок с
Код: Выделить всё
sleep(100)
. Сейчас перевариваю уже который месяц (исходники выложил для демонстрации), кусок со sleep повторно искать не буду, но задержка осталась.
wavebvg
постоялец
 
Сообщения: 354
Зарегистрирован: 28.02.2008 04:57:35

Re: Размер стека для потока

Сообщение hinst » 10.05.2014 13:37:37

А ну не знаю, я реализацию для Windows смотрел, там видимо нету
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Размер стека для потока

Сообщение xdsl » 12.05.2014 13:04:17

Никакого Sleep(100) в linux-версии тоже не нашел. fpc 2.6.2
В каком исходнике?

Добавлено спустя 22 минуты 52 секунды:
И да, 100 потоков прекрасно запускается и завершается. Например, следующий код работает ожидаемо быстро:
Код: Выделить всё
{$mode objfpc}
uses cthreads,sysutils,dateutils;

const MaxThreads=300;

function increment(parameter: pointer):PtrInt;
  begin   result:=0;  end;

var ths:array [1..MaxThreads] of TThreadID;
     dt:tdatetime; i:ptruint;
begin
dt:=now();
for i:=1 to MaxThreads do ths[i]:=BeginThread(@increment,pointer(i));
for i:=1 to MaxThreads do WaitForThreadTerminate(ths[i],0);

writeln('Общее время работы: ',millisecondsbetween(now(),dt));

end.
xdsl
постоялец
 
Сообщения: 131
Зарегистрирован: 15.01.2009 13:49:03

Re: Размер стека для потока

Сообщение wavebvg » 12.05.2014 23:14:02

Смутил вот этот участок кода:
fpc/rtl/unix/tthread.inc
Код: Выделить всё
function TThread.WaitFor: Integer;
begin
  WRITE_DEBUG('waiting for thread ',ptruint(FHandle));
  If (MainThreadID=GetCurrentThreadID) then
    {
     FFinished is set after DoTerminate, which does a synchronize of OnTerminate,
     so make sure synchronize works (or indeed any other synchronize that may be
     in progress)
    }
    While not FFinished do
      CheckSynchronize(100);
  WaitFor := WaitForThreadTerminate(FHandle, 0);
  { should actually check for errors in WaitForThreadTerminate, but no }
  { error api is defined for that function                             }
  FThreadReaped:=true;
  WRITE_DEBUG('thread terminated');
end;


В Вашем примере (да и моих попытках воспроизведения), пока что не получается воспроизвести по аналогии. А вот если необходима синхронизация с основным потоком, тогда и возникают проблемы:

Код: Выделить всё
{$mode objfpc}
uses cthreads,sysutils,dateutils,classes;

const MaxThreads=300;

type

  { TMyThread }

  TMyThread = class(TThread)
  private
    FTime: Integer;
  protected
    procedure Execute; override;
    procedure SyncOnTerminated(Sender: TObject);
  public
    constructor Create(CreateSuspended: Boolean; const StackSize: SizeUInt = DefaultStackSize);
    property Time: Integer read FTime write FTime;
  end;

function increment(parameter: pointer):PtrInt;
  begin  Sleep(Integer(parameter));  end;

var ths:array [1..MaxThreads] of TThreadID;
     dt:tdatetime; i:ptruint;
var ths1:array [1..MaxThreads] of TMyThread;

{ TMyThread }

procedure TMyThread.Execute;
begin
  Sleep(Time);
end;

procedure TMyThread.SyncOnTerminated(Sender: TObject);
begin
end;

constructor TMyThread.Create(CreateSuspended: Boolean; const StackSize: SizeUInt);
begin
  inherited;
end;

begin
  dt:=now();
  for i:=1 to MaxThreads do ths[i]:=BeginThread(@increment,pointer(i*10));
  for i:=1 to MaxThreads do WaitForThreadTerminate(ths[i],0);
  writeln('Общее время работы: ',millisecondsbetween(now(),dt));

  for i:=1 to MaxThreads do
  begin
    ths1[i]:=TMyThread.Create(true);
    ths1[i].Time:=i*10;
    ths1[i].OnTerminate := @ths1[i].SyncOnTerminated;
  end;
  dt:=now();
  for i:=1 to MaxThreads do ths1[i].Resume;
  for i:=1 to MaxThreads do ths1[i].WaitFor;
  writeln('Общее время работы: ',millisecondsbetween(now(),dt));
  for i:=1 to MaxThreads do ths1[i].Free;

end.


Общее время работы: 3012
Общее время работы: 3101


Зачем ждать эти злосчастные 100 мсек? Хотя в моих потугах в прошлом посте ошибка с задержкой не исчезает - нужно менять подход...
wavebvg
постоялец
 
Сообщения: 354
Зарегистрирован: 28.02.2008 04:57:35

Re: Размер стека для потока

Сообщение xdsl » 15.05.2014 12:28:50

Что-то до меня не дошло, где Вам приходится ждать 100мс. Если Вы про CheckSynchronize(100), то там - блокировка на событии (SynchronizeTimeoutEvent), с таймаутом. Т.е. если событие возникает, таймаут не пригождается. Задержки могут быть, если мне память не изменяет, только в случае если сейчас выполняется метод Synchronize для эксклюзивной работы с GUI.

Что касается Вашего второго примера, то Вы там сами делаете Sleep, в исполняемых методах. Естественно, что нити не завершат работу, пока не закончат каждая свой Sleep. Уберите его, и получите время работы от 10 до 200 мс на каждый набор из 300 потоков.
xdsl
постоялец
 
Сообщения: 131
Зарегистрирован: 15.01.2009 13:49:03

Re: Размер стека для потока

Сообщение wavebvg » 15.05.2014 22:17:25

xdsl писал(а):Что касается Вашего второго примера, то Вы там сами делаете Sleep, в исполняемых методах. Естественно, что нити не завершат работу, пока не закончат каждая свой Sleep. Уберите его, и получите время работы от 10 до 200 мс на каждый набор из 300 потоков.

Ну... Без задержки я получаю какое-то минимальное время, говорящее о времени создания/завершения потока + время на синхронизацию основного потока:
не [10-200], а [10-90]U[110-190], что намекает на то, что в этот странный цикл синхронизации можно легко и непринуждённо попасть. Прошу прощения на своё упорство, просто не вижу необходимости обрабатывать этот цикл:

Код: Выделить всё
While not FFinished do
      CheckSynchronize(100);

Ситуация, когда мы вызываем синхронизацию из основного потока и при этом из основного же потока вызывается WaitFor мне кажется очень неестественной.

1. Из основного потока вызываем JOIN
2. Второй поток может завершиться и вызвать синхронизацию с основным
3. Раз в 100мс основной просыпается, и если надо - выполняет код OnTerminate, если второй поток завершится
4. После чего вызывается DoTerminate, где меняется флаг
5. Цикл завершается

На мой взгляд это несколько странно, да и самый простой способ использования, когда пользователь в OnTerminate будет обрабатывать очередь, приводит к тому, что пул потоков в 300 ЗАВЕРШАЕМЫХ потоков простоит... 3 секунды (!!!) из-за синхронизации...

С другой стороны, понятно, что это делается для того, чтобы при количестве потоков > 2 (1 основной и 2 вторичных), можно было бы основной поток заткнуть в JOIN для первого вторичного, и при этом второй вторичный мог бы вызвать и обработать OnTerminate...

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

PS. Мне тоже кажется странным, откуда возникают эти 100мс, ведь вызов CheckSynchronize(100) не должен создавать задержек, которые явно возникают!!!
wavebvg
постоялец
 
Сообщения: 354
Зарегистрирован: 28.02.2008 04:57:35

Re: Размер стека для потока

Сообщение hinst » 18.06.2014 13:54:39

Кто хочет: есть реализация кроссплатформенного потока от MSEIDE-MSEGUI: https://gitorious.org/mseide-msegui/mse ... thread.pas

Добавлено спустя 7 минут 25 секунд:
в MSE библиотеки как-то немного более структурированы: есть отдельно какие-то модули: mseclasses, msestream, mselist, msethread, mseevent, а не всё в Classes с include-файлами
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Размер стека для потока

Сообщение Cheb » 30.06.2014 16:46:20

Delphi, кстати, передаёт 0 в BeginThread в качестве размера стека, что интерпретируется как "такой же размер стека, как и для основного потока".

Вот во время оно я стал изучать потоки и выяснилось, что Windows 98 гарантированно падает, если ей передавать 0. Причём, не просто программа, а винда целиком. Я тогда это как баг FPC докладывал, объяснял, что ружья кирпичом не чистят, и надо передавать хоть какое-то значение.

Прислушались, значит. :D
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Размер стека для потока

Сообщение Sergei I. Gorelkin » 01.07.2014 12:45:34

Поведение CreateThread с нулевым размером стека документировано в MSDN и не менялось со времен Windows 95. Если бы оно роняло всю винду, ни одна программа на Дельфи не работала бы. А они работали, и их было много... некоторые работают и до сих пор.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Размер стека для потока

Сообщение wavebvg » 07.07.2014 17:36:20

Так, случайно набрёл на замечание по теме для unix стека.
wavebvg
постоялец
 
Сообщения: 354
Зарегистрирован: 28.02.2008 04:57:35

Пред.

Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 19

Рейтинг@Mail.ru
cron