NJU-ProjectN / nvboard

NJU Virtual Board
Other
198 stars 38 forks source link

探讨提升依赖nvboard的项目编译性能的可能 #32

Closed Wenz-jam closed 1 month ago

Wenz-jam commented 1 month ago

问题提出

以项目中的example为例,example 将nvboard.a 作为库被 verilator 识别并链接,以此提供 nvboard 的相关功能.但是这样引入了一个问题: nvboard.a只是被 verilator 认为是外部的库而不会加入依赖项中,也就是说仅仅是 nvboard.a 的改变不会引起 main.cpp 的重新编译.
example中似乎使用删除veriator生成的相关代码并重新编译来避免这个问题

# example/Makefile:34-39
$(BIN): $(VSRCS) $(CSRCS) $(NVBOARD_ARCHIVE)
+++ @rm -rf $(OBJ_DIR) <--- 这里删除了obj_dir
    $(VERILATOR) $(VERILATOR_CFLAGS) \
        --top-module $(TOPNAME) $^ \
        $(addprefix -CFLAGS , $(CXXFLAGS)) $(addprefix -LDFLAGS , $(LDFLAGS)) \
        --Mdir $(OBJ_DIR) --exe -o $(abspath $(BIN))

对于example这样较小的项目这样做是没有问题的,但是如果Verilog部分的代码比较庞大(如ysyxSoC),删除重新编译的方法的耗时是相当巨大的,此时必须依赖ccache来提升编译性能,同时意外发现,有时候ccahce并没有起到作用,导致编译非常的折磨人.

因此我希望在此探讨提升依赖nvboard项目编译性能可能的办法:

尝试的方案

diff --git a/.gitignore b/.gitignore
index 0c7970f..5511ce6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@
 !*.v
 !*.cpp
 build/
+/usr/
\ No newline at end of file
diff --git a/include/pins.h b/include/pins.h
index 6075a29..d48d045 100644
--- a/include/pins.h
+++ b/include/pins.h
@@ -1,7 +1,7 @@
 #ifndef __PINS_H__
 #define __PINS_H__

-#include <../usr/include/pins.h>
+#include <usr/pins.h>
 #include <stdint.h>
 #include <assert.h>

diff --git a/usr/include/nvboard.h b/include/usr/nvboard.h
similarity index 100%
rename from usr/include/nvboard.h
rename to include/usr/nvboard.h
diff --git a/usr/include/pins.h b/include/usr/pins.h
similarity index 100%
rename from usr/include/pins.h
rename to include/usr/pins.h
diff --git a/scripts/nvboard.mk b/scripts/nvboard.mk
index 5144118..7f75e51 100644
--- a/scripts/nvboard.mk
+++ b/scripts/nvboard.mk
@@ -21,6 +21,8 @@ $(NVBOARD_BUILD_DIR)/%.o: $(NVBOARD_SRC)/%.cpp
 $(NVBOARD_ARCHIVE): $(NVBOARD_OBJS)
    @echo + AR "->" $(shell realpath $@ --relative-to $(NVBOARD_HOME))
    @ar rcs $(NVBOARD_ARCHIVE) $(NVBOARD_OBJS)
+   @mkdir -p $(NVBOARD_USR_INC)
+   @cp --no-preserve=timestamps $(NVBOARD_INC)/usr/* $(NVBOARD_USR_INC)

 # Rule (`#include` dependencies): paste in `.d` files generated by gcc on `-MMD`
 -include $(NVBOARD_OBJS:.o=.d)

最开始的想法为在 $(NVBOARD_ARCHIVE) 的编译过程中添加 touch $(NVBOARD_USR_INC)/* , 通过 $(NVBOARD_USR_INC)/{nvboard,pins}.h 的改变来告知make需要重新编译那些引用了nvboard.a的文件.
但是我发现pins.h同时被外部的程序与nvboard自生引用.因此 touch 的方案会导致对nvboard的大量的不必要的编译.因此有了这样的修改.将NVBOARD_USR_INC 移动到 include/usr , nvboard自身使用include/usr 中的内容而nvboard对外则依然使用 NVBOARD_USR_INC , 同时使用 cp --no-preserve=timestamps 来保证 NVBOARD_USR_INC 内容的更新.

修改带来的问题

性能对比

我的makefile, 无rm obj_dir

$(BIN):$(TOP_MK) $(CSRC) $(HSRC) $(NVBOARD_ARCHIVE)
    @echo + MAKE "->" npc[$(notdir $(BIN))]
    make -C $(OBJ_DIR) -f $<

$(TOP_MK):$(VSRCS)
    @$(VERILATOR) --top-module $(TOP) $(SRCS) $(NVBOARD_ARCHIVE) \
        --cc --exe\
        -CFLAGS -I$(abspath ./include)\
        $(addprefix -CFLAGS , $(CFLAGS))\
        $(addprefix -LDFLAGS , $(LDFLAGS)) \
        $(addprefix -I, $(VSRC_INCLUDE_PATH))\
        --no-timing --timescale "1ns/1ns" --clk clock\
        --threads 1 --threads-dpi pure\
        -j\
        --trace-fst\
        $(addprefix +define+,$(DEFINE))\
        --Mdir $(OBJ_DIR) -o $(BIN)

参照example中的方式进行编译的代码, 有rm obj_dir

 $(BIN):$(SRCS) $(NVBOARD_ARCHIVE)
    @rm -rf $(OBJ_DIR)
    @$(VERILATOR) --top-module $(TOP) $(SRCS) $(NVBOARD_ARCHIVE) \
        --cc --exe --build\
        -CFLAGS -I$(abspath ./include)\
        $(addprefix -CFLAGS , $(CFLAGS))\
        $(addprefix -LDFLAGS , $(LDFLAGS)) \
        $(addprefix -I, $(VSRC_INCLUDE_PATH))\
        --no-timing --timescale "1ns/1ns" --clk clock\
        --threads 1 --threads-dpi pure\
        -j\
        --trace-fst\
        $(addprefix +define+,$(DEFINE))\
        --Mdir $(OBJ_DIR) -o $(BIN)

测试指令: time make -j OBJCACHE="" 我这里强行不使用ccache来展示性能差异 测试流程为: make clean -> time make -> touch nvboard/src -> time make

Executed in usr time sys time
第一次 11.18 secs 26.10 secs 1.23 secs
第二次 1.18 secs 1.39 secs 0.23 secs
Executed in usr time sys time
第一次 11.10 secs 25.63 secs 1.43 secs
第二次 12.43 secs 29.72 secs 1.76 secs

可见性能提升相当明显.同时我makefile的写法要求nvboard.a nvboard.h pins.h同时发生改变,因此我认为对nvboard的相关修改是有一定可行性的.

sashimi-yzh commented 1 month ago

我还没理解你的需求, 为什么nvboard.a改变的时候需要重新编译main.cpp?

Wenz-jam commented 1 month ago

我的理解是nvboard.a被main.cpp静态链接.因此需要重新编译链接. 最小的测试就是注释掉 example 中的 rm obj_dir 那一行(35)与nvboard.cpp中的nvboard_update函数中的所有内容. 期望的效果是这样的(如果main.cpp被重新链接): image 但是实际上的效果是这样的(依然能正常工作): image

Wenz-jam commented 1 month ago

我的需求就是我写的makefile中不应该有rm obj_dir那一行,因为他会导致编译非常费时间.而rm obj_dir是nvboard导致的

sashimi-yzh commented 1 month ago

那行rm估计是前期verilog修改较多的时候方便测试用的, 和nvboard本身无关, 可以直接删掉.

sashimi-yzh commented 1 month ago

我的理解是nvboard.a被main.cpp静态链接.因此需要重新编译链接.

你这个理解不正确.

Wenz-jam commented 1 month ago

那行rm估计是前期verilog修改较多的时候方便测试用的, 和nvboard本身无关, 可以直接删掉.

但是删掉以后他的确行为是非预期的

sashimi-yzh commented 1 month ago

nvboard.a变化分两种情况讨论:

  1. 修改了nvboard中和API无关的代码. 这种情况下可以兼容之前的main.cpp, 所以不需要重新编译main.cpp
  2. 修改了nvboard中和API相关的代码, 这种情况下无法兼容之前的main.cpp, 因此还需要手动修改main.cpp. 但Makefile可以追踪到main.cpp的更新, 因此可以自动重新编译main.cpp

因此, 只要Makefile追踪了main.cpp的更新, 就不需要在Makefile中针对nvboard.a再额外考虑main.cpp的更新问题(所以我还不理解你的需求).

真正需要考虑的是链接(我觉得你可能没有理解编译和链接的区别, 如果是的话, 可以去搜索相关资料), 因为nvboard.a变化之后, 肯定要重新生成最后的可执行文件, 但这只需要实现以下两点

  1. 将nvboard.a加到可执行文件生成规则的依赖里面
  2. 在nvboard相关的cpp文件更新时重新生成nvboard.a. 这一点example没有实现

因此, 你真正需要做的是在Makefile中编写正确的依赖关系, 和上文提到的rm命令无关.

PS: 这个example不是我写的, 是学生写的, 那行rm命令最早在 https://github.com/NJU-ProjectN/nvboard/commit/fbb53d914265c8ca3b7741a6f92a27a06a1cd62f 中添加

Wenz-jam commented 1 month ago

在nvboard相关的cpp文件更新时重新生成nvboard.a

这个显然是实现了的,他不是由example实现的,而是由script/nvboard.mk实现的

将nvboard.a加到可执行文件生成规则的依赖里面

问题就在这里,我不知道是我设置的问题还是verilator的问题,verilator所生成的makefile的依赖里面不会包含nvboard.a 至少到最新的v5.024都没有这样做过.
那么为了实现这一点,我就需要大改我的makefile,难道您不觉得这是一件非常奇怪的事情吗? 那么现在看来我的需求就是: 我不希望nvboard引起我makefile的大改.

Wenz-jam commented 1 month ago

不过我现在倒是觉得这是verilator没有处理好这个问题了

sashimi-yzh commented 1 month ago

直接把nvboard.a作为verilator的命令行参数就可以了啊, 我的makefile完全没有大改.

我用的verialtor是5.012, 但感觉老版本应该也支持.

Wenz-jam commented 1 month ago

这样看来应该就是我设置出了问题了,抱歉打扰您这么长时间. 关闭issue