【问题标题】:How to create a mini-benchmark program in C如何在 C 中创建一个迷你基准程序
【发布时间】:2016-09-16 14:07:36
【问题描述】:

我有一个任务,我需要编写一个基准程序来测试具有两种排序算法(一种迭代算法和一种递归算法)的任何处理器的性能。事情是我的老师告诉我我必须创建三个不同的程序(即 3 个 .c 文件),每个排序算法两个(它们都必须从用 \n 分隔的文本文件中读取整数并写入相同的数字到另一个文本文件但已排序)和基准测试程序。在基准程序中,我需要使用公式 MIP = NI/T*10^6 计算 MIP(每秒百万条指令),其中 NI 是指令数,T 是执行这些指令所需的时间。我必须能够通过计算每个算法的 MIP,然后求解 T 的方程来估计每个算法在任何处理器上花费的时间,例如 EstimatedTime = NI/MIPs*10^6。 我的问题是......我如何准确地衡量一个程序与另一个程序的性能?我从来没有做过这样的事情。我的意思是,我想我可以使用 C 中的 TIME 函数并测量执行 X 行和东西的时间,但只有当所有 3 个函数(2 个排序算法和 1 个基准函数)都在同一个程序中时,我才能做到这一点.我什至不知道如何开始。

哦,顺便说一句,我必须通过交叉编译从 C 到 MIPS(asm 语言)的排序算法并计算使用了多少指令来计算指令数。

任何指南都将不胜感激...我目前有这些功能:

  • readfile(读取带有整数的文本文件)
  • 写文件
  • 排序算法

【问题讨论】:

  • 某处应该有问题..
  • 交叉编译到汇编器可以让您计算编译器生成的指令数量,但您需要计算出每条指令以某种方式执行了多少次。
  • 这是一个问题“我如何准确地衡量一个程序与另一个程序的性能?”。是的,你是对的......如果我有一个循环,那将取决于输入的大小(将排序多少个数字)。我没有想到那个大声笑......现在我真的不知道该怎么办哈哈。我想我可以计算循环外的行数,然后将循环内的指令数乘以计数器。例如 i*24,其中 i 是计数器(i++ 和东西),24 是每个周期执行的指令数。
  • “我如何准确地衡量一个程序与另一个程序的性能?” - 就这样?好吧,等几个星期,我会有一篇关于那个的论文......
  • 我不知道如何用另一个 .c 文件来衡量一个 .c 文件的执行时间。我只能想到一个包含或类似的东西......

标签: c algorithm sorting benchmarking


【解决方案1】:

在 Linux 系统上,您可以使用硬件性能计数器:perf stat ./a.out 并获得周期、指令、缓存未命中和分支错误预测的准确计数。 (也可以使用其他计数器,但这些是默认计数器)。

这为您提供动态指令计数,计算循环内的指令实际运行的次数。

交叉编译 MIPS 和计数指令很容易为您提供静态指令计数,但实际上需要遵循 asm 的工作原理来确定每个循环运行的次数。

【讨论】:

  • 我知道如何阅读 MIPS,所以我想我可以做到这一点,但如果有一个工具可以做到这一点,那就更好了,无论如何,它会帮助我测试我自己的计算。虽然我对 Linux 不熟悉......我的老师给了我这个命令来从 C 转换为 MIPS,但我不知道如何使用它,它说 a.out 不存在:mipsel-linux-gnu -objdump -d ./a.out
  • @sebasura: 如果你不使用-oa.out 是 gcc 输出二进制文件的默认名称。在 Windows 上,gcc 将其称为 a.exe,但在其他平台上则称为 a.out。用你所谓的二进制文件替换它。 (即如果你使用gcc ... -o foo,那么它就是foo)。
【解决方案2】:

如何编译多个文件并将它们链接在一起取决于编译器。以 GCC 为例,它可能很简单

gcc -O3 -g3  -W -Wall -Wextra main.c sortalog1.c sortalgo_2.c [...] sortalgo_n.c -o sortingbenchmark

这不是最常见的方法,但对于这项任务来说已经足够了。

如果您想计算操作码,最好将各个 c 文件单独编译为 ASM。对要分析汇编器输出的每个 C 文件执行以下操作:

gcc -c -S sortalgo_n.c

不要忘记将您的函数声明放入一个通用头文件中,并在您使用它们的任何地方包含它!

对于基准测试:您确实知道每个 C 操作的 ASM 操作数量,并且可以将这个计数映射到 C 代码的每一行,尽管这并不容易。如果你有这个,你所要做的就是增加一个计数器。例如:如果一行 C 代码转换为 123 个 ASM 操作码,则将计数器增加 123。

您可以使用一个全局变量来执行此操作。如果每个排序算法使用多个线程,则需要注意添加是原子的(使用 _Atomic 或互斥锁或您的操作系统/编译器/库提供的任何内容)。

顺便说一句:这看起来是一种非常精确的测量运行时间的方法,但并不是每个 ASM 操作码在现实世界中的 CPU 上运行的周期数都相同。今天不需要打扰,但你应该记住明天。

【讨论】:

  • 每个“C 操作”的 asm 指令数不是一个常数,取决于周围的代码。 OP 为 MIPS CPU 进行交叉编译并在内循环内计数指令的方法听起来比尝试自己从某些东西中推断更理智。
  • 我一开始也有类似的想法,但由于说明发生了变化,我不知道该怎么做。所以是的,我认为在循环内计数指令更好,但我不知道如何将循环计数器从算法 C 文件捕获到基准 C 文件。顺便说一句,我必须用不同的 -OX 标志对相同的算法进行基准测试,并估计每种情况下的执行时间。我认为这也会改变从 C 语言翻译的 MIPS 指令的数量...
  • @PeterCordes OP 写道,MIPS 操作码的数量是已知的,所以我认为整个事情都是纯粹的学术程序。但从上面的评论看来,这似乎是一个非常现实的练习,远非简单的功课。
  • @sebasura:是的,优化级别会改变很多。 O0 通常比其他任何东西都要糟糕得多,并且在每个 C 语句之后将所有内容都存储到内存中(例如,您可以使用调试器修改变量)。比较 -O3 和 -O0 处的简单函数 on the Godbolt compiler explorer (MIPS gcc 5.4)。甚至 -O1 和 O3 一样好,因为没有内联或复杂的循环转换,但 O2 和 O3 在更大的函数中确实很重要。
  • 是的,我需要在一台真实的计算机上实际测试它,在 Linux 上进行整个编译(使用 Docker,我也必须学习)等等。我的主要问题是如何从另一个程序计算一个程序中执行的 MIP。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-13
  • 2019-05-02
  • 1970-01-01
  • 1970-01-01
  • 2018-11-16
  • 1970-01-01
相关资源
最近更新 更多