компилировать одновременно, ссылаться последовательно - PullRequest
2 голосов
/ 07 марта 2019

У меня большой проект cmake / c ++ / linux: много маленьких статических библиотек, несколько больших и взаимозависимых статических библиотек, несколько больших исполняемых двоичных файлов. Один двоичный файл с символами отладки составляет несколько ГБ. Существует ~ 10 таких двоичных файлов (рабочий, testA, testB, testC ...). Компиляция обычно занимает больше времени, чем хотелось бы, но у нас есть сервер быстрой сборки и мы используем make -j20. Самое худшее - это ссылки. Одиночное связывание занимает около 60 секунд и 4 ГБ ОЗУ. Но когда все окончательные двоичные файлы связаны одновременно (случается часто, когда была изменена 1 небольшая вложенная библиотека, мало для перекомпиляции, много для перекомпоновки), 10 компоновщиков используют 40 ГБ ОЗУ (для одного разработчика может быть больше) и очень долго , IO, скорее всего, является узким местом.

У нас много разработчиков на 1 сильном сервере, и все используют make -j20 -l30, чтобы мы не перегружали процессор. Но у нас нет метода для ограничения количества одновременных линкеров. Было бы здорово ограничить количество работающих линкеров глобально на сервере, но каждый вызов make также помог бы. В идеале make -j20 -l30 --concurrent-linkers=2. Возможно ли это?

Мы используем золотой линкер. Мы занимаемся разделением небольших независимых модулей, но это займет много времени.

1 Ответ

0 голосов
/ 07 марта 2019

Вы можете попробовать что-то вроде:

$ cat Makefile
OBJS := foo bar baz...
EXES := qux quux quuz...

.PHONY: all

all: $(OBJS)
    $(MAKE) -j $(concurrent-linkers) $(EXES)

$(OBJS): ...
    <compile>

$(EXES): ...
    <link>

И назовите это с:

$ make -j20 -l30 concurrent-linkers=2

По сути, он разделяет сборку на два вызова make, один для компиляции и один для ссылки, с различными параметрами -j. Основным недостатком является то, что все компиляции должны быть закончены до начала первой ссылки. Лучшим решением было бы спроектировать простой сервер заданий на связывание (простой сценарий оболочки с небольшим количеством flock и файлами тегов сделал бы его) и делегировать ему задания на связывание. Но если вы можете жить с этим ...

Демо с фиктивным Makefile:

$ cat Makefile
OBJS := a b c d e f
EXES := u v w x y z

.PHONY: all

all: $(OBJS)
    $(MAKE) -j $(concurrent-linkers) $(EXES)

$(OBJS) $(EXES):
    @printf 'building $@...\n' && sleep 2 && printf 'done\n' && touch $@
$ make -j20 -l30 concurrent-linkers=2
building a...
building d...
building b...
building c...
building e...
building f...
done
done
done
done
done
done
make -j 2 u v w x y z
make[1]: warning: -jN forced in submake: disabling jobserver mode.
make[1]: Entering directory 'foobar'
building u...
building v...
done
done
building w...
building x...
done
done
building y...
building z...
done
done
make[1]: Leaving directory 'foobar'

Как видите, все $(OBJS) цели строятся параллельно, в то время как $(EXES) цели строятся 2 (максимум) за раз.

РЕДАКТИРОВАТЬ Если ваш make-файл сгенерирован CMake, есть как минимум две опции:

  1. Настройте ваши файлы CMake так, чтобы CMake генерировал два разных make-файла: один для компиляции и один для ссылки. Затем напишите простой make-файл обертки, например:

    .PHONY: myAll myCompile
    
    myAll: myCompile
        $(MAKE) -j $(concurrent-linkers) -f Makefile.link
    
    myCompile:
        $(MAKE) -f Makefile.compilation
    
  2. Убедите CMake (если это еще не так) сгенерировать make-файл, который определяет две переменные make: одну (OBJS), равную списку всех объектных файлов, и одну (EXES), равную список всех исполняемых файлов. Затем напишите простой make-файл обертки, например:

    .DEFAULT_GOAL := myAll
    
    include CMake.generated.Makefile
    
    .PHONY: myAll
    
    myAll: $(OBJS)
        $(MAKE) -j $(concurrent-linkers) $(EXES)
    

    Очень похожее решение существует, если вместо этого CMake генерирует две фальшивые цели, одну для всех объектных файлов и другую для всех исполняемых файлов:

    .DEFAULT_GOAL := myAll
    
    include CMake.generated.Makefile
    
    .PHONY: myAll
    
    myAll: cmake-target-for-compilation
        $(MAKE) -j $(concurrent-linkers) cmake-target-for-link
    
...