【问题标题】:Change older makefile system to take advantage of parallel compiles更改旧的 makefile 系统以利用并行编译
【发布时间】:2011-02-01 16:48:19
【问题描述】:

我们使用 Microsoft NMAKE 编译大量原生 C++ 和一些 Intel Fortran 文件。通常,makefile 包含这样的行(对于每个文件):

$(LINKPATH)\olemisc.obj : ole2\olemisc.cpp $(OLEMISC_DEP)
$(CCDEBUG) ole2\olemisc.cpp
$(GDEPS) ole2\olemisc.cpp

OLEMISC_DEP =\
e:\ole2\ifaceole.hpp\
e:\ole2\cpptypes.hpp\
等等

它工作正常,但一次编译一个文件。我们想利用多核处理器并一次编译多个文件。我将不胜感激有关实现这一目标的最佳方法的一些建议。这是我目前所拥有的。

一个: GNU make 允许您使用 --jobs=2 选项执行并行作业,例如,它适用于 GCC(遗憾的是,我们不能使用 GCC)。但微软的 NMAKE 似乎并不支持这样的选择。这两个名称程序的兼容性如何,如果我们确实开始使用 GNU MAKE,你能同时运行两个 cl.exe 进程吗?我希望他们抱怨 PDB(调试)文件被锁定,或者更新的 cl.exe 命令行参数之一是否可以解决这个问题?

二: cl.exe 有一个 /MP(多进程构建)标志,如果通过命令行一起传递,它可以让您同时编译多个文件,例如:

cl /MP7 a.cpp b.cpp c.cpp d.cpp e.cpp

但使用它需要更改生成文件。我们的 make 文件是由我们自己的程序从其他文件生成的,因此我可以轻松更改我们放入 makefile 中的内容。但是如何在 makefile 中将来自不同 cpp 文件的依赖项组合在一起,以便通过一个 cl.exe 调用将它们一起编译?每个 .obj 都是一个不同的目标,有一组命令来实现它?

或者我是否将 makefile 更改为不调用 cl.exe,而是我们编写的其他一些小可执行文件,然后将一系列 .cpp 文件收集在一起,然后向 cl.exe 传递多个参数?这会起作用并且似乎可行,但也似乎过于复杂,我看不到其他人这样做。

我是否遗漏了一些明显的东西?一定有更简单的方法来完成这个吗?

我们不使用 Visual Studio 或解决方案文件进行编译,因为文件列表很广泛,我们的 makefile 中有一些特殊项目,理论上不希望过度绑定到 MS C++ 等。

【问题讨论】:

    标签: makefile compilation parallel-processing


    【解决方案1】:

    我强烈推荐 Windows 上的 GNU make。我倾向于使用 cygwin make,因为它创建的环境往往非常可移植到类 Unix 平台(首先是 Mac 和 Linux)。使用 Microsoft 工具链进行并行编译,并具有 100% 准确的依赖项和 CPU 使用率,效果非常好。不过,您还有其他要求。

    就您的nmake 问题而言,请在手册中查找batch-mode inference rules。基本上,nmake 能够调用 C 编译器一次,一次将一整套 C 文件传递​​给它。因此您可以使用编译器的/MP... 类型开关。

    编译器内置并行编译?呸!我说非常糟糕。无论如何,这是一个骨架:

    OBJECTS = a.obj b.obj c.obj
    f.exe: $(OBJECTS)
        link $** -o $@
    
    $(OBJECTS): $$(@R).c
    
    # "The only syntactical difference from the standard inference rule
    # is that the batch-mode inference rule is terminated with a double colon (::)."
    .c.obj::
        cl -c /MP4 $<
    

    编辑

    如果每个 .obj 都有自己的依赖项(很可能!),那么您只需将它们添加为单独的依赖项行(即,它们没有附加任何 shell 命令)。

    a.obj: b.h c.h ../include/e.hpp
    b.obj: b.h ../include/e.hpp
        ∶
    

    此类样板通常由另一个工具生成,并将!INCLUDEd 生成到主makefile 中。如果您很聪明,那么您可以在编译时免费生成这些依赖项。 (如果你走到这一步,那么nmake 开始在接缝处吱吱作响,你应该换成 GNU make。)

    【讨论】:

      【解决方案2】:

      这里要记住的另一个注意事项是:您基本上必须为每个路径和扩展定义一个批处理规则。但是,如果您在两个不同的源目录中有两个同名的文件,并且对这两个目录都有一个批处理推理规则,那么批处理规则可能不会选择您想要的那个。

      基本上,make 系统知道它需要制作某个 obj 文件,并且一旦找到允许它这样做的推理规则,它就会使用它。

      解决方法是不要有重复的命名文件,如果无法避免,请不要对这些文件使用推理或批处理规则。

      【讨论】:

        【解决方案3】:

        好的,今天早上我花了一些时间来解决这个问题,多亏了 bobbogo,我才开始工作。以下是正在考虑此问题的其他人的确切详细信息:

        一次编译一个文件的旧式makefile有很多这样的:

        $(LINKPATH)\PS_zlib.obj : zlib\PS_zlib.cpp $(PS_ZLIB_DEP)
                $(CC) zlib\PS_zlib.cpp
        
        $(LINKPATH)\ioapi.obj : zlib\minizip\ioapi.c $(IOAPI_DEP)
                $(CC) zlib\minizip\ioapi.c
        
        $(LINKPATH)\iowin32.obj : zlib\minizip\iowin32.c $(IOWIN32_DEP)
                $(CC) zlib\minizip\iowin32.c
        

        请注意,每个文件一次编译一个。因此,现在您想使用花哨的 Visual Studio 2010 /MP 开关“/MP[n] 最多使用 'n' 个进程进行编译”来同时编译多个文件。如何?你的makefile需要用到nmake中的批量推理规则,如下:

        $(LINKPATH)\PS_zlib.obj : zlib\PS_zlib.cpp $(PS_ZLIB_DEP)
        
        $(LINKPATH)\ioapi.obj : zlib\minizip\ioapi.c $(IOAPI_DEP)
        
        $(LINKPATH)\iowin32.obj : zlib\minizip\iowin32.c $(IOWIN32_DEP)
        
        #Batch inference rule for extension "cpp" and path "zlib":
        {zlib}.cpp{$(LINKPATH)}.obj::
                $(CC) $(CCMP) $<
        
        #Batch inference rule for extension "c" and path "zlib\minizip":
        {zlib\minizip}.c{$(LINKPATH)}.obj::
                $(CC) $(CCMP) $<
        

        在这种情况下,在其他地方,我们有

        CCMP = /MP4
        

        请注意,nmake 推理批处理规则不支持路径中的通配符或空格。我在某处发现了一些不错的 nmake 文档,其中指出您需要为每个扩展名和源文件位置创建单独的规则,如果文件位于不同的位置,则不能有一个规则。另外,使用#import 的文件不能用/MP 编译。

        我们有一个生成我们的 makefile 的工具,所以它现在也生成批量推理规则。

        但它有效!编译一个大 dll 的时间从 12 分钟缩短到 7 分钟!哇哦!

        【讨论】:

        • 有人给我一个“这很有用”的勾号,以便给你所有的细节,好吗? 笑容
        • 这里要记住的另一个考虑是:您基本上必须为每个路径和扩展定义一个批处理规则。但是,如果您在两个不同的源目录中有两个同名文件,批处理规则可能不会选择您想要的那个。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-25
        • 2016-05-10
        • 2013-08-04
        • 1970-01-01
        • 2012-03-07
        • 2013-08-21
        相关资源
        最近更新 更多