2010-06-19 67 views
1

我有一個簡單的程序程序。轉換程序爲多線程,利用多核cpu

Procedure TForm1.btnKeywrdTransClick(Sender: TObject); 
Var 
    i, ii    : integer; 
    ch_word, zword, uy_word: widestring; 
Begin 
    TntListBox1.items.LoadFromFile('d:\new folder\chh.txt'); //Chinese 
    TntListBox2.items.LoadFromFile('d:\new folder\uyy.txt'); //Uyword 
    TntListBox4.items.LoadFromFile(Edit3.text); //list of poi files 
    For I := 0 To TntListBox4.items.Count - 1 do 
    Begin 
    TntListBox3.items.LoadFromFile(TntListBox4.Items[i]); 
    zword := tntlistbox3.Items.Text;  //Poi 
    For ii := 0 To TntListBox1.Items.count - 1 Do 
    Begin 
    loopz; 
    ch_word := tntlistbox1.Items[ii]; 
    uy_word := ' ' + TntListBox2.items[ii] + ' '; 
    zword := wideFastReplace(zword, ch_word, uy_word, [rfReplaceAll]); //fastest, and better for large text 
    End; 
    TntListBox3.Items.text := zword; 
    TntListBox3.items.SaveToFile(TntListBox4.Items[i]); 
end; 
end; 

現在我的新電腦有4cores,使得這一程序的多線程將使其運行速度更快(如果我使用4個線程,每核心線程)? 我沒有多線程的經驗,我需要你的幫助 謝謝。

PS:這是LOOPZ過程

Procedure loopz; 
Var 
    msg    : tmsg; 
Begin 
    While PeekMessage(Msg, 0, 0, 0, pm_Remove) Do 
    Begin 
    If Msg.Message = wm_Quit Then Halt(Msg.WParam); 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
    End; 
End; 

更新1:答案 ,我也會做

1 - 使用一個分析器,以找到最耗時的代碼

2 - 儘可能消除與gui相關的東西

3 - 使用線程。

我會報告回來。謝謝大家。

回答

2

它看起來像你與GUI元素接口。

所有GUI代碼的99%必須從一個且僅有一個線程接口。

如果您重構代碼以在一系列線程中執行文本替換,將它們之間的文本分開,然後讓GUI線程將其放入列表框中,則可以提高性能。

請注意,創建和同步線程並不便宜。除非有成千上萬的條目可以使用,否則您可能會通過添加線程來減慢程序速度。

4

首先,您應該剖析您的代碼,以查看從TntListBox讀取是否會減慢您的速度,或者它是否爲WideFastReplace。但即使在此之前,請刪除'loopz'電話 - 它最大程度地減慢了你的速度!爲什麼你在這個循環內處理消息呢?

要找到瓶頸,只需將您的循環計時兩次,但第二次註釋WideFastReplace調用。 (並確保你計時只循環,不分配給TntListBox3或保存到文件或加載從文件)。

時候你就會知道什麼是放慢你失望,報告回來......

BTW ,並行調用WideFastReplace幾乎是不可能的,因爲它總是在同一個源上運行。我沒有看到任何並行化代碼的好方法。

一個可能的並行處理方法:

  • 拆分zword在一個恰當的詞分隔符(我假設在這裏你只更換的話,不是短語)爲N串N爲內核的數量。
  • 爲這些N個字符串中的每一個並行執行全部替換(所有搜索/替換對)。當然,你必須首先從TntListBoxes中讀取搜索/替換對到一些內部結構(TStringList就足夠了),然後在所有N個線程中使用這個結構。
  • 將這些部分字符串連接在一起。

當然,如果WideFastReplace不是代碼的耗時部分,那麼這樣做沒有意義。先進行剖析!

+0

有一個簡單的方法來並行化代碼,看看我的答案。最大的問題是給名單上的專有名稱,以便每個人都能看到並理解該算法實際上在做什麼! – 2010-06-19 08:19:21

+0

@Cosmin:代碼執行zword:= replace(zword,a,b,[all]),並沒有簡單的方法來並行化。 – gabr 2010-06-19 08:50:46

+0

你不會並行化;您將第一個循環(經過文件名的循環)並行化。 – 2010-06-19 08:57:37

1

您應該通過僅使用一個線程來獲得相當多的改進。有了這個,你可以完全省略掉loopz

請注意,您應該使用例程中的本地TWideStringList實例替換TntListboxes。

當您對多線程有所瞭解時,可以將工作分成多個線程。這可以通過將多個(比如3-4個)列表中的poi文件列表(listbox4)分開來完成,每個線程一個列表。

14

首先使算法在當前版本中一樣有效:停止使用TListBox來存儲數據!(抱歉大喊)用TStringList替換它們,你會得到一個巨大的性能改善。這是所需的第一步,因爲你不能使用來自多個線程的GUI對象(實際上你只能從「主」線程使用它們)。當您將TListBox更改爲TStringList 時,請給出您的變量有意義的名稱。我不知道有多少人在這裏發現你在ListBox4中存儲文件名列表,在ListBox3中加載每個文件,使用ListBox1作爲「關鍵字列表」,ListBox2作爲「值列表」...真的,這是一個大混亂!下面是它會是什麼樣子用的TStringList和專有名詞:

Procedure TForm1.btnKeywrdTransClick(Sender: TObject); 
Var 
    i, ii    : integer; 
    ch_word, zword, uy_word: widestring; 
    PoiFilesList:TStringList; // This is the list of files that need work 
    PoiFile:TStringList; // This is the file I'm working on right now 
    KeywordList, ValueList:TStringList; // I'll replace all keywords with corresponding values 
Begin 

    PoiFilesList := TStringList.Create; 
    PoiFile := TStringList.Create; 
    KeywordList := TStringList.Create; 
    ValueList := TStringList.Create; 

    try 
    PoiFilesList.LoadFromFile(Edit3.text); //list of poi files 
    KeywordList.LoadFromFile('d:\new folder\chh.txt'); //Chinese 
    ValueList.LoadFromFile('d:\new folder\uyy.txt'); //Uyword 
    For I := 0 To PoiFilesList.Count - 1 do 
    Begin 
     PoiFile.LoadFromFile(PoiFilesList[i]); 
     zword := PoiFile.Text;  //Poi 
     For ii := 0 To KeywordList.count - 1 Do 
     Begin 
     ch_word := KeywordList[ii]; 
     uy_word := ' ' + ValueList[ii] + ' '; 
     zword := wideFastReplace(zword, ch_word, uy_word, [rfReplaceAll]); 
     End; 
     PoiFile.text := zword; 
     PoiFile.SaveToFile(PoiFilesList[i]); 
    end; 
    finally 
    PoiFilesList.Free; 
    PoiFile.Free; 
    KeywordList.Free; 
    ValueList.Free; 
    end; 
end; 

如果你現在的代碼看,很明顯它做什麼,這是明顯如何多線程它。你有一個包含文件名稱的文本文件。您打開這些文件中的每一個,並將所有關鍵字替換爲相應的值。將文件保存回磁盤。這很容易!將KeywordList和ValueList加載到內存一次,將文件列表拆分爲4個較小的列表,啓動4個線程,每個線程使用它自己的較小文件列表。

我不想扭轉代碼的整個多線程變體,因爲如果我自己寫它,你可能不知道它是如何工作的。如果你遇到麻煩,給它一個機會並尋求幫助。

+1

+1;我希望我可以爲其他好主意添加更多+(命名,嘗試......最終)。 – 2010-06-19 10:13:57

+0

嗨, 我已經有這段代碼的TWideStringList(來自Mike Lischke的unicode.pas)版本。是的它更快。 但我需要現場觀看結果,所以我不得不使用列表框;) – avar 2010-06-19 10:18:10

+0

另一件事,我不直接在列表框中更改字符串,我將字符串移動到zword以獲得一些速度。 – avar 2010-06-19 10:27:28

1

可以並行運行的操作受益於多任務 - 那些必須一個接一個運行的操作不能。操作越大,益處就越大。在你的過程中,你可以並行化文件加載(儘管我猜他們沒有那麼多元素),並且你可以並行化具有多個線程的替換操作,每個線程在不同的列表元素上操作。運行速度取決於文件大小。 我想你在使用GUI元素來存儲數據而不是直接在內存結構上工作時速度更快,因爲這意味着經常重新繪製控件,這是一個昂貴的操作。

-1

以下是您的答案 1.如果可以,請不要等到用戶點擊以對操作做出反應。通過 將它們放在包裝對象 之前,像在formcreate上一樣運行它在一個線程下運行;一旦完成,將其標記爲可以使用 當用戶單擊該操作時,檢查標記。如果不這樣做
又做了while循環,等待像

btnKeywrdTrans.Enabled := False; 
while not wrapper.done do 
begin 
    Sleep(500); 
    Application.Processmessages; 
end; 
..... your further logic 
btnKeywrdTrans.Enabled := True; 
  • 用的TStringList時,更換或TWideStringList
  • 乾杯 範