converting program to Multithreading, taking advantage of multicore cpu
i have a simple program with one procedure.
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 TntList开发者_运维问答Box4.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;
now my new computer has 4cores, is making this program multithreading will make it run faster (if i uses 4 thread, a thread per core) ? i have no experience with multithreading, i need your help thanks.
ps : this is Loopz procedure
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;
update 1 : from the answers, im gonna do
1 - use a profiler to find the most time consuming code
2 - try eliminate gui related things if possible
3 - use threads.
i'll report back . thanks all.
First of all make the algorithm as effective as it can be in it's current incarnation: Stop using TListBox to store your data!!! (sorry for shouting) Replace them with TStringList and you'll get a HUGE performance improvement. That's an required first step any way, because you can't use GUI objects from multiple threads (in fact you may only use them from the "main" thread). While you're changing TListBox to TStringList please give your variable meaningful names. I don't know how many people around here figured out that you're storing a list of file names in ListBox4, loading each file in ListBox3, using ListBox1 as a "keyword list" and ListBox2 as a "value list"... really, it's a big mess! Here's how it would look like with TStringList and proper names:
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;
If you look at the code now, it's obvious what it does, and it's obvious how to multi-thread-it. You've got a text file containing names of files. You open up each one of those files and replace all Keywords with the corresponding Values. You save the file back to disk. It's easy! Load the KeywordList and ValueList to memory once, split the list of files into 4 smaller lists, start up 4 threads each working with it's own smaller files list.
I don't want to writhe the whole multi-threaded variant of the code because if I'll write it myself you might not understand how it works. Give it a chance and ask for help if you get into trouble.
First you should profile your code to see if reading from TntListBox is slowing you down or if it is WideFastReplace. But even before that, remove the 'loopz' call - it is slowing you the most! Why are you processing messages inside this loop at all?
To find the bottleneck, simply time your loop twice, but the second time comment out the WideFastReplace call. (And make sure you are timing only the loop, not the assignment to the TntListBox3 or saving into file or loading from file.)
When you will know what's slowing you down, report back ...
BTW, calling WideFastReplace in parallel would be almost impossible as it is always operating on the same source. I don't see any good way to parallelize your code.
A possible parallelization approach:
- Split zword on an appropriate word delimiter (I'm assuming here you are only replacing words, not phrases) into N strings where N is the number of cores.
- Do the full replacement (all search/replacement pairs) for each of those N strings in parallel. Of course, you would have to read search/replacement pairs first from the TntListBoxes into some internal structure (TStringList would suffice) and then use this structure in all N threads.
- Concatenate those partial strings back together.
Of course, there's no point in doing that if WideFastReplace is not the time-consuming part of the code. Do the profiling first!
It looks like you are interfacing with GUI elements.
99% of all GUI code must be interfaced from one and only one thread.
If you refactor your code to perform the text replacements in a series of threads, dividing the text amongst them, and then have the GUI thread place it into your list box, you could improve performance.
Note that creating and synchronizing threads is not cheap. Unless you have thousands of entries to work on, you will likely slow down your program by adding threads.
You should gain quite a bit of improvement by using only one thread for the whole thing. With this you can omit the loopz
call completely.
Be aware that you should replace the TntListboxes with local TWideStringList instances in your routine.
When you have gotten somewhat familiar with multithreading, you can go and split the work into multiple threads. This can be done for instance by splitting the list of poi files (listbox4) in multiple (say 3-4) lists, one for each thread.
Operations that could be run in parallel benefit from multitasking - those that have to be run one after another can't. The larger the operation, the larger the benefit. In your procedure you could parallelize the file loadings (although I guess they hold not so many elements) and you could parallelize the replace operation having multiple threads operating each on different list elements. How much faster it will run depends of the files size. I guess you have more speed penality in using GUI elements to store data instead of working directly on in-memory structure, because you that means redrawing the controls often, which is an expensive operation.
Here is your answer
1. If you can, do not wait until user click to react to the action. Do it before hand like on formcreate by
Put them into wrapper object
Run it under a thread; once finish, mark it to be ready to be used
When user click on the action, check for marker. If it is not done
yet do a while loop and wait something like
btnKeywrdTrans.Enabled := False;
while not wrapper.done do
begin
Sleep(500);
Application.Processmessages;
end;
..... your further logic
btnKeywrdTrans.Enabled := True;
- Replace it with TStringList or TWideStringList
Cheers Pham
精彩评论