【问题标题】:Delphi: Speed up loading of interfaced plugins (DLL)Delphi:加速加载接口插件(DLL)
【发布时间】:2014-03-22 06:37:04
【问题描述】:

几天前,SO 成员帮助我创建了一个安全的插件系统,使用接口在主应用程序和 dll 之间进行通信。这解决了我在访问冲突和内存泄漏方面遇到的一些问题,现在一切正常,没有错误或崩溃。

所以,我一直在为这个项目创建一些长期到期的插件,这导致我遇到另一个问题:速度

我现在正在做的是,当主应用程序启动时,将所有 dll 加载到遵循确定名称模式的特定文件夹中。

我用来加载它们的代码如下:

if FindFirst(cfg.ExePath+cPathPlugins+'\np*.npl', faAnyFile, SR)<>0
   then Exit; // npl files are in fact renamed dll's

PluginHost := TPluginHost.Create as IPluginHost;
Plugins    := TObjectList<TPluginInfo>.Create(True);

repeat
  if (SR.Attr <> faDirectory)
  then begin
    dll := LoadLibrary(PChar(cfg.ExePath+cPathPlugins+SR.Name));
    if dll<>0
    then begin
      @PluginInit := GetProcAddress(dll, 'PluginInitialize');
      if Assigned(PluginInit)
      then begin
        Plugin := TPluginInfo.Create;
        try
          Plugin.Dll  := dll;
          Plugin.Intf := PluginInit(PluginHost);
          Plugins.Add(Plugin);
        except
          Plugin.Free;
        end;
      end
      else FreeLibrary(dll);
    end;
  end;
until FindNext(SR)<>0;
System.SysUtils.FindClose(SR);

这段代码大约需要 45 秒来加载 7 个插件。这些 dll 都没有初始化代码,PluginInitialize 只是传递主机接口并检索插件接口。

我的问题是:

  1. 此时接口上的方法数量会影响加载速度吗?我不相信,但想确认一下。

  2. 能否以某种方式缩短此加载时间,同时在启动时仍加载它们?

我确实想到了一个替代方案,将插件的名称加载到数据库中并仅在第一次使用时加载 dll 本身,但我不想应用这个,因为其中一些插件必须自动运行在应用程序执行期间完成某些事件,而不仅仅是通过按需菜单选项。

我想也许这可以在后台加载插件,在不同的线程上完成,但我不知道这是否会带来任何风险,因为我从未使用过线程。 我相信使用线程的主要风险是当一个人试图访问正在被另一个人修改的变量时,这是对的吗? 在这种情况下,我认为不会发生这种情况,因为插件加载之后会获取插件名称(使用其方法之一)并将其添加到 TButtonGroup 中,该 TButtonGroup 是在开始搜索 dll 之前创建的。

您认为这是一个好的解决方案吗? 如果没有,您可以指点我的任何替代方案吗?

【问题讨论】:

  • 不调试时也一样?无论如何,你必须确定什么需要时间。 Profile。但是有了这个时机,您甚至可以手动配置文件。只需跨过 (F8) 每一行,观察哪个语句明显变慢。如有必要,然后进入 (F7)。等等。
  • 正如@Sertac 所说,我们无法为您调试,因为我们没有您正在加载的 DLL 或您用于加载它们的整个代码。自己调试代码,或者找一个分析器(谷歌“profiler Delphi”)可以帮助你定位问题。无论如何,您的问题太宽泛了;这里的一般规则是“每个帖子一个特定的问题”,并且您已经问了四个(两个您已经编号,另外两个在您的最后一段中)根本不具体。
  • @nunopicado 我猜你可能正在编译你的“插件”而不使用包。 DLL 文件有多大?
  • 是的,我没有使用运行时包。每个 Dll 大约 12mb。
  • 为什么插件需要 12MB?

标签: multithreading performance delphi dll interface


【解决方案1】:

您的问题是 DLL 很大。您需要使用运行时包创建 DLL。这样,代码只加载一次。每个 DLL 都将包含相同代码的副本。 LoadLibrary 将加载 DLL 并为每个 DLL 调用初始化代码。这意味着包 X 将被链接到每个使用它的插件中,并在每个插件加载时被初始化。 (更正

对于独立的 EXE 文件,去掉运行时包是很棒的。它使部署更加简单。当您想开始使用插件系统时,最好切换到包含运行时包的系统。

这并不意味着您需要将每个运行时包分开。例如,如果您只在主应用程序或单个插件中使用 Dev Express 控件,那么您可以让 Delphi 将该包编译到 App/DLL 中。

要更改您希望单独保存哪些运行时包以及您希望在项目中包含哪些运行时包,请转到项目选项中的“包-运行时包”页面。有一个复选框可让您选择与运行时包链接。下面是一个文本框。在此文本框中,您可以指定要分开的包的名称。

【讨论】:

  • 或者将所有插件中的公共代码溢出到它们都可以共享的资源 DLL 中。从宿主进程加载这个 DLL,并将其句柄传递给每个插件 DLL 以使用
  • 是的,这也有帮助。看着 12 兆的大小,我有一种偷偷摸摸的感觉,就像 DevExpress 控件一样,增加了很多。
  • 是的,任何类型的库都可能导致文件过大。这意味着您必须在任何地方确定您的 uses 子句,并确保您没有使用任何您不需要的东西
  • 巨大?加载库时不会运行 12MB 代码,它只是映射到地址空间。我认为第一段没有任何意义。当然,“PluginInit”可能需要一些时间,但有待确定,并且在使用包时可能不会有太大变化。
  • @SertacAkyuz 加载 DLL 时,会执行 DLL 中包含的所有单元的初始化代码。 LoadLibrary 还将为每一个将完整的 12 Meg 加载到 RAM 中。我更新了第一段以使其更清晰。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-01
  • 2011-06-13
相关资源
最近更新 更多