2010-06-19 80 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
  • 干杯 范