Вопрос о скорости цикла Delphi

Существует ли более быстрый путь? Я в основном должен добавить AA-ZZ к тысячам записей за один раз.

Просто список 35 объектов занимает долгое время для завершения muchless список тысячи.


procedure Tmainform.btnSeederClick(Sender: TObject);
var
  ch,ch2:char;
  i:integer;
  slist1, slist2:TStrings;
begin
  slist1:= TStringList.Create;
  slist2:= TStringList.Create;
  slist1.Text :=queuebox.Items.Text;
  for ch := 'a' to 'z' do
    begin
      for ch2 := 'a' to 'z' do
        begin
          //

      for I := 0 to slist1.Count - 1 do
        begin
        application.ProcessMessages; // so it doesn't freeze the application in long loops.  Not 100% sure where this should be placed, if at all.
         sleep(1);  //Without this it doesn't process the cancel button.
         if cancel then Break; 
         slist2.Add(slist1.Strings[i]+ch+ch2);
        end;
    end;
end;
insertsingle(slist2,queuebox);
freeandnil(slist1);
freeandnil(slist2);

конец;

Спасибо за любую справку

5
задан Brad 20 January 2010 в 04:15
поделиться

9 ответов

ОК. Я пытался оптимизировать ваш код. Для окончательных тестов требуется некоторые тестовые данные.

Что я сделал (он включает в себя большую часть идей из Мейсона):

  • Замечайте код о «Отмена» и «
  • дали типы и переменные более значимое имя
  • Использовали имена, которые Delphi использует («приложение» вместо «приложения» и т. Д.) Чтобы сделать его читаемым
  • , переместили некоторую логику в «keamuigoing»
  • , переместите расчет суффиксов из основного цикла в контуру инициализации
  • Он необязательно использует TStringBuilder (который может быть быстрее, чем TSTringList, и доступен, поскольку Delphi 2009)

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

procedure TForm2.Button1Click(Sender: TObject);
{$define UseStringBuilder}
  procedure KeepUIGoing(SourceListIndex: Integer);
  begin
    if SourceListIndex mod 100 = 0 then
    begin
      Application.ProcessMessages;
      // so it doesn't freeze the application in long loops.  Not 100% sure where this should be placed, if at all.
      Sleep(1);
    end;
  end;
const
  First = 'a';
  Last = 'z';
type
  TRange = First .. Last;
  TSuffixes = array [TRange, TRange] of string;
var
  OuterIndex, InnerIndex: Char;
  SourceListIndex: Integer;
  SourceList, TargetList: TStrings;
  Suffixes: TSuffixes;
  NewLine: string;
{$ifdef UseStringBuilder}
  TargetStringBuilder: TStringBuilder; // could be way faster than TStringList
{$endif UseStringBuilder}
begin
  for OuterIndex := First to Last do
    for InnerIndex := First to Last do
      Suffixes[OuterIndex, InnerIndex] := OuterIndex + InnerIndex;

  SourceList := TStringList.Create;
  TargetList := TStringList.Create;
{$ifdef UseStringBuilder}
  TargetStringBuilder := TStringBuilder.Create();
{$endif UseStringBuilder}
  try
    SourceList.Text := queuebox.Items.Text;
    for OuterIndex := First to Last do
    begin
      for InnerIndex := First to Last do
      begin
        for SourceListIndex := 0 to SourceList.Count - 1 do
        begin
          KeepUIGoing(SourceListIndex);
          // if cancel then
          // Break;
          NewLine := SourceList.Strings[SourceListIndex] + Suffixes[OuterIndex, InnerIndex];
{$ifdef UseStringBuilder}
          TargetStringBuilder.AppendLine(NewLine);
{$else}
          TargetList.Add(NewLine);
{$endif UseStringBuilder}
        end;
      end;
    end;
{$ifdef UseStringBuilder}
    TargetList.Text := TargetStringBuilder.ToString();
{$endif UseStringBuilder}
    // insertsingle(TargetList, queuebox);
  finally
{$ifdef UseStringBuilder}
    FreeAndNil(TargetStringBuilder);
{$endif UseStringBuilder}
    FreeAndNil(SourceList);
    FreeAndNil(TargetList);
  end;
end;

- Jeroen

- Jeroen

4
ответ дан 18 December 2019 в 05:19
поделиться

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

MyObject().DoBar();

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

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

-121--4585768-

0 используется в качестве часового значения для обозначения нулевого или нулевого кода. аналогично тому, как указатели могут быть NULL в C. без дозора, вам потребуется дополнительный бит, чтобы проверить, был ли установлен inode в структуре или нет.

Подробнее здесь:

Все адреса блоков и inode начинаются с 1. Первым блоком на диске является блок 1. 0 используется для обозначения отсутствия блок. (Разреженные файлы могут иметь внутри них)

http://uranus.chrysocome.net/explore2fs/es2fs.htm

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

-121--1644037-

Существует несколько очевидных проблем с кодом.

Во-первых, вы тратите много циклов ЦП, вычисляя одни и те же значения снова и снова. Значения AA.. ZZ не будут меняться, поэтому нет необходимости строить их снова и снова. Попробуйте сделать следующее: Создайте третий список TStringList. Проходите и заполните его всеми возможными AA.. ZZ перестановок с вашим двойным циклом. Как только это закончится, выполните цикл и объедините этот список предварительно вычисленных последовательностей со значениями в slist1 . Вы должны увидеть довольно большой толчок от этого.

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

Во-вторых, и это, вероятно, то, что убивает вас, вы не должны иметь ProcessMessages и Sleep вызовы в самом внутреннем цикле. Сон (1); звучит так, будто это означает «сон в течение 1 миллисекунды», но Windows не предлагает такой точности. В итоге вы получаете «сон не менее 1 миллисекунды». Он освобождает ЦП до тех пор, пока к нему не вернется Windows, которая обычно находится где-то порядка 16 миллисекунд. Таким образом, вы добавляете задержку в 16 мс (плюс до тех пор, пока ProcessMessages занимает) в очень жесткий цикл, который, вероятно, занимает всего несколько микросекунд , чтобы выполнить остальную часть своего кода.

Если вам нужно что-то подобное, чтобы поддерживать отклик пользовательского интерфейса, он должен находиться в самой внешней петле, а не во внутренней,и вам, вероятно, даже не нужно запускать его каждую итерацию. Попробуйте что-то вроде , если ch mod 100 = 0, то//sleep и обработайте сообщения здесь . Предложение Крейга перенести эту задачу в рабочий поток также помогло бы, но только если вы знаете достаточно о потоках, чтобы сделать это правильно. Они могут быть хитрыми.

14
ответ дан 18 December 2019 в 05:19
поделиться

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

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

-121--1315284-

int, char, float, double и т.д. все имеют фиксированную длину в памяти. например, int имеет 4 байта, таким образом 32 бита.

но последовательность может иметь различную длину, это фактически массив символов.

-121--1315283-

Чтобы остановить дополнительную обработку TStringList, необходимо окружить код slist2.BeginUpdate () и slist2.EndUpdate () .

Из моего опыта вы получите очень большое улучшение, используя меньше ProcessMessages (); Спи (1); высказывания, как предложено в других ответах.

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

11
ответ дан 18 December 2019 в 05:19
поделиться

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

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

Некоторые преимущества многопотативного:

  • Основная нить остается отзывчивым.
  • Расчет может быть остановлен по желанию.

и Окормирование некоторых подводных камней:

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

Вставить ниже код в нашем любимом редакторе, и вы должны быть хороши, чтобы пойти.

procedure TForm1.btnStartClick(Sender: TObject);
var
  I: Integer;
begin
  //***** Fill the sourcelist
  FSource := TStringList.Create;
  FDestination := TStringList.Create;
  for I := 0 to 9999 do
    FSource.Add(Format('Test%0:d', [I]));

  //***** Create and fire Thread
  FSeeder := TSeeder.Create(FSource, FDestination);
  FSeeder.OnTerminate := DoSeederDone;
  FSeeder.Resume;
end;

procedure TForm1.btnStopClick(Sender: TObject);
begin
  if Assigned(FSeeder) then
    FSeeder.Terminate;
end;

procedure TForm1.DoSeederDone(Sender: TObject);
var
  I, step: Integer;
begin
  I := 0;
  step := 0;
  while I < FDestination.Count do
  begin
    //***** Don't show every item. OutputDebugString is pretty slow.
    OutputDebugString(PChar(FDestination[I]));
    Inc(step);
    Inc(I, step);
  end;
  FSource.Free;
  FDestination.Free;
end;

{ TSeeder }

constructor TSeeder.Create(const source, destination: TStringList);
begin
  //***** Create a suspended, automatically freed Thread object.
  Assert(Assigned(source));
  Assert(Assigned(destination));
  Assert(destination.Count = 0);
  inherited Create(True);
  FreeOnTerminate := True; //***** Triggers the OnTerminate event
  FSource := source;
  FDestination := destination;
end;

procedure TSeeder.Execute;
var
  I, J: Integer;
  AString: string;
begin
  FDestination.BeginUpdate;
  try
    FDestination.Capacity := FSource.Count * 26 * 26;
    for I := 0 to Pred(FSource.Count) do
    begin
      AString := FSource[I];
      for J := 0 to Pred(26 * 26) do
      begin
        FDestination.Add(AString + Char(J div 26 + $41) + Char(J mod 26 + $41));
        if Terminated then Exit;
      end;
    end;
  finally
    FDestination.EndUpdate;
  end;
end;
5
ответ дан 18 December 2019 в 05:19
поделиться

Попробуйте этот образец кода - надеюсь, что это поможет немного (Delphi 5 int./winxp) )

procedure TForm1.Button1Click(Sender: TObject);
var
   i,k: Integer;
   sourceList, destList: TStringList;
   ch1, ch2: char;
begin
   destList := TStringList.Create;
   sourceList := TStringList.Create;

   //some sample data but I guess your list will have 1000+
   //entries?
   sourceList.Add('Element1');
   sourceList.Add('Element2');
   sourceList.Add('Element3');

   try
      i := 0;
      while i < (26*26) do
      begin
         if (i mod 100) = 0 then
            Application.ProcessMessages;

         ch1 := char(65 + (i div 26));
         ch2 := char(65 + (i mod 26));

         for k := 0 to sourceList.Count -1 do
            destList.Add(Format('%s-%s%s', [sourceList.Strings[k], ch1, ch2]));
         Inc(i);
      end;

      Memo1.Lines.AddStrings(destList);
   finally
      FreeAndNil(destList);
      FreeAndNil(sourceList);
   end;
end;    

---reinhard

1
ответ дан 18 December 2019 в 05:19
поделиться

Я бы увидишь, сможете ли вы сделать это в одной петле согласно комментарию. Также попробуйте сделать это в потоке, чтобы вы могли устранить application.processmessages и вызовы сна, не блокируя пользовательский интерфейс.

4
ответ дан 18 December 2019 в 05:19
поделиться

Я знаю, что это не конкретно отвечает на ваш вопрос, но если вы заинтересованы в Delphi Algorithm's Julian Bucknall (CTO devexpress ), написал окончательные алгоритмы Delphi Algorithms

Томы Delphi: алгоритмы и структуры данных :

  • Глава 1: Что такое алгоритм?
  • Глава 2: Массивы
  • Глава 3: Связанные списки, стеки и очереди
  • Глава 4 : Поиск
  • Глава 5: Сортировка
  • Глава 6: Рандомизированные алгоритмы
  • Глава 7: HASHING и HASH Tables
  • Глава 8: Двоичные деревья
  • Глава 9: Очески приоритетов и Heapsort
  • Глава 10 : Государственные машины и регулярные выражения
  • Глава 11: Сжатие данных
  • Глава 12: Расширенные темы

Вы также можете получить его EZDSL (Easy Libletry Data Structures) для Delphi 2009 и Delphi 6-2007 .

1
ответ дан 18 December 2019 в 05:19
поделиться

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

procedure Tmainform.btnSeederClick(Sender: TObject);
var
  ch,ch2:char;
  i:integer;
  slist1, slist2:TStrings;
begin
  slist1:= TStringList.Create;
  slist2:= TStringList.Create;

  slist1.Text :=queuebox.Items.Text;

  slist2.BeginUpdate() 
     for I := 0 to slist1.Count - 1 do
        begin
        application.ProcessMessages; // so it doesn't freeze the application in long loops.  Not 100% sure where this should be placed, if at all.
         if cancel then Break; 
         slist2.Add(slist1.Strings[i]+'AA');
         slist2.Add(slist1.Strings[i]+'AB');
         slist2.Add(slist1.Strings[i]+'AC');
         ...
         slist2.Add(slist1.Strings[i]+'ZZ');
        end;
slist2.EndUpdate()
insertsingle(slist2,queuebox);
freeandnil(slist1);
freeandnil(slist2);
end;
-1
ответ дан 18 December 2019 в 05:19
поделиться

Если вы хотите, чтобы во время цикла обрабатывались события, например, нажатие кнопки Cancel, достаточно вызвать Application.ProcessMessages. Если вы будете вызывать этот вызов регулярно, но не слишком регулярно, например, 50 раз в секунду, то ваше приложение будет оставаться отзывчивым на нажатие кнопки Cancel, не замедляясь слишком сильно. Application.ProcessMessages возвращается довольно быстро, если нет сообщений для обработки.

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

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

1
ответ дан 18 December 2019 в 05:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: