在此C代码中goto语句是否不可避免?

时间:2019-01-10 01:05:37

标签: c goto

如果不使用goto语句,我将无法编写代码。他们难免吗?

我写了这样的代码:

sequence 是一个函数,可打印所有由 m 个数字组成的,从0到(n-1)的序列并返回它们的编号。

它可以按预期工作,但我在其中使用了三个标签和三个goto语句。

我也没有使用goto语句编写了 sequence_substitute ,但是当n> 9时,它比 sequence 慢。

Int?

有没有什么方法可以不使用goto语句而像 sequence 一样快地编写函数?

4 个答案:

答案 0 :(得分:3)

我在以下代码中对这两个函数进行了基准测试。

在此基准测试中,(m,n)=(6,15);

    #include <stdio.h>
    #include <stdlib.h>

    double get_current_time();

    int sequence(int m, int n);
    int sequence_substitute(int m, int n);

    double benchmark(int (*method)(int, int), int m, int n) {
        double time1 = get_current_time();
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        double time2 = get_current_time();
        return (time2 - time1) / 10;
    }

    int main(void) {
        const int m = 6;
        const int n = 15;
        fprintf(stderr, "sequence: %f\n", benchmark(sequence, m, n));
        fprintf(stderr, "sequence_substitute: %f\n",
                benchmark(sequence_substitute, m, n));
        return 0;
    }

    #if defined(WIN32) || defined(__WIN32) || defined(_WIN32) || \
        defined(__WIN32__) || defined(_WIN32_)
    #include <windows.h>

    double get_current_time() {
        LARGE_INTENGER t, f;
        QueryPerformanceCounter(&t);
        QueryPerformanceFrequency(&f);
        return (double)t.QuadPart / (double)f.QuadPart;
    }

    #else

    #include <sys/resource.h>
    #include <sys/time.h>

    double get_current_time() {
        struct timeval t;
        gettimeofday(&t, 0);
        return t.tv_sec + t.tv_usec * 1e-6;
    }

    #endif

    /**************************************************************************/

    // this prints all sequences that consist of m numbers from 0 to (n - 1), and
    // return the number of them. for example, if m = 2 and n = 3 then it prints "0
    // 0 / 0 1 / 0 2 / 1 0 / 1 1 / 1 2 / 2 0 / 2 1 / 2 2 / " and return 9.
    int sequence(int m, int n) {
        int i[100], j = 0, count = 0;
    A:
        if (!(j < m)) {
            for (int k = 0; k < m; ++k) printf("%d ", i[k]);
            printf("/ ");
            ++count;
            goto C;
        }
        i[j] = 0;
    B:
        if (i[j] < n) {
            ++j;
            goto A;
        }
    C:
        --j;
        if (j >= 0) {
            ++i[j];
            goto B;
        }
        putchar('\n');
        return count;
    }

    int sequence_substitute(int m, int n) {
        int i[100], count = 0;
        for (int j = 0; j < m; ++j) i[j] = 0;
        for (;;) {
            int j = m - 1;
            for (int k = 0; k < m; ++k) printf("%d ", i[k]);
            printf("/ ");
            ++count;
            for (;;) {
                if (i[j] < n - 1) {
                    ++i[j];
                    break;
                } else {
                    if (j == 0) {
                        putchar('\n');
                        return count;
                    } else {
                        i[j] = 0;
                        --j;
                    }
                }
            }
        }
    }

https://gist.github.com/yuchiki/26fd96a2791f7f6d2d9929b404a16da6

其结果如下:

当使用-O3编译时,

  • 序列:5.390164 [秒]
  • sequence_substitute:5.381983 [秒]

,并且使用-O0编译时,

  • 序列:5.178518 [sec]
  • sequence_substitute:5.256273 [秒]

结果表明,即使没有进行任何优化,这两个函数也以几乎相同的速度计算结果。

也许,您在此处显示的代码太含糊,无法重现您报告的速度差异。

要更精确地讨论该现象,向我们显示以下信息可能会有用:

  • 完整而准确的代码,包括 main ,任何编译指示或指令以及基准代码
  • 基准测试结果

我通过以下测试代码针对这两个函数的无IO版本尝试了另一个基准测试:


    #include <stdio.h>
    #include <stdlib.h>

    double get_current_time();

    int sequence(int m, int n);
    int sequence_substitute(int m, int n);

    double benchmark(int (*method)(int, int), int m, int n) {
        double time1 = get_current_time();
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        method(m, n);
        double time2 = get_current_time();
        return (time2 - time1) / 10;
    }

    int main(void) {
        const int m = 7;
        const int n = 15;
        fprintf(stderr, "sequence: %f\n", benchmark(sequence, m, n));
        fprintf(stderr, "sequence_substitute: %f\n",
                benchmark(sequence_substitute, m, n));
        return 0;
    }

    #if defined(WIN32) || defined(__WIN32) || defined(_WIN32) || \
        defined(__WIN32__) || defined(_WIN32_)
    #include <windows.h>

    double get_current_time() {
        LARGE_INTENGER t, f;
        QueryPerformanceCounter(&t);
        QueryPerformanceFrequency(&f);
        return (double)t.QuadPart / (double)f.QuadPart;
    }

    #else

    #include <sys/resource.h>
    #include <sys/time.h>

    double get_current_time() {
        struct timeval t;
        gettimeofday(&t, 0);
        return t.tv_sec + t.tv_usec * 1e-6;
    }

    #endif

    /**************************************************************************/

    // this prints all sequences that consist of m numbers from 0 to (n - 1), and
    // return the number of them. for example, if m = 2 and n = 3 then it prints "0
    // 0 / 0 1 / 0 2 / 1 0 / 1 1 / 1 2 / 2 0 / 2 1 / 2 2 / " and return 9.
    int sequence(int m, int n) {
        int i[100], j = 0, count = 0;
    A:
        if (!(j < m)) {
            for (int k = 0; k < m; ++k) {
            }  // printf("%d ", i[k]);
            // printf("/ ");
            ++count;
            goto C;
        }
        i[j] = 0;
    B:
        if (i[j] < n) {
            ++j;
            goto A;
        }
    C:
        --j;
        if (j >= 0) {
            ++i[j];
            goto B;
        }
        // putchar('\n');
        return count;
    }

    int sequence_substitute(int m, int n) {
        int i[100], count = 0;
        for (int j = 0; j < m; ++j) i[j] = 0;
        for (;;) {
            int j = m - 1;
            for (int k = 0; k < m; ++k) {
            }  // printf("%d ", i[k]);
            // printf("/ ");
            ++count;
            for (;;) {
                if (i[j] < n - 1) {
                    ++i[j];
                    break;
                } else {
                    if (j == 0) {
                        // putchar('\n');
                        return count;
                    } else {
                        i[j] = 0;
                        --j;
                    }
                }
            }
        }
    }

结果如下:

当使用-O3编译时,

  • 序列:0.019198 [sec]
  • sequence_substitute:0.016234 [sec]

当使用-O0编译时,

序列:0.136406 [秒] sequence_substitute:0.112287 [秒]

我认为-O3版本的结果没有太大意义,因为在这种情况下可以猜测大多数代码已被优化程序删除。但是-O0版本建议以下事实:

  • 此代码中最重的部分是IO。
  • sequence_substitute 的逻辑部分也与 sequence 的逻辑部分一样快。

答案 1 :(得分:1)

我就像他们的yuchiki中的answer一样,对代码进行了基准测试。我还提出了自己的解决方案。

我在运行2.9 GHz Intel Core i7,运行macOS 10.14.2 Mojave,使用自制GCC 8.2.0的MacBook Pro(15英寸,2017年)上运行了测试,并在{ GitHub上的{3}}(堆栈溢出问题)存储库,为SOQ子目录中的文件timer.ctimer.h。我将sequence函数从问题重命名为sequence_withgoto,以使其名称与其他函数相似。我删除了(通过注释掉了)序列生成器函数中的打印代码。我将计数器类型从int更改为unsigned(尽管有人可以说应该/应该是unsigned long long才能提供更大的范围)。下面显示的最大测试恰好恰好溢出了32位unsigned类型,给出的答案为0。

免注释测试代码(源文件seq23.c):

#include <assert.h>
#include <stdio.h>
#include "timer.h"

static unsigned sequence_withgoto(int m, int n)
{
    int i[100], j = 0;
    unsigned count = 0;
A:
    if (!(j < m))
    {

        ++count;
        goto C;
    }
    i[j] = 0;
B:
    if (i[j] < n)
    {
        ++j;
        goto A;
    }
C:
    --j;
    if (j >= 0)
    {
        ++i[j];
        goto B;
    }

    return count;
}

static unsigned sequence_substitute(int m, int n)
{
    int i[100];
    unsigned count = 0;
    for (int j = 0; j < m; ++j)
        i[j] = 0;
    for ( ; ; )
    {
        int j = m - 1;

        ++count;
        for ( ; ; )
        {
            if (i[j] < n - 1)
            {
                ++i[j];
                break;
            }
            else
            {
                if (j == 0)
                {

                    return count;
                }
                else
                {
                    i[j] = 0;
                    --j;
                }
            }
        }
    }
}

static unsigned generate_sequence(int m, int n)
{
    assert(m <= n);
    assert(m > 0);
    assert(n < 20);

    int data[m];
    for (int i = 0; i < m; i++)
        data[i] = 0;

    unsigned counter = 0;
    while (data[0] < n)
    {

        counter++;
        for (int i = m - 1; i >= 0; i--)
        {
            if (++data[i] < n)
                break;
            if (i == 0)
                break;
            data[i] = 0;
        }
    }

    return counter;
}

static void time_sequence_generator(const char *tag, int m, int n, unsigned (*function)(int m, int n))
{
    Clock clk;

    clk_init(&clk);
    clk_start(&clk);
    unsigned count = (*function)(m, n);
    clk_stop(&clk);
    char buffer[32];
    printf("Number of sequences (m = %d, n = %d): %u elapsed = %s (%s)\n",
           m, n, count, clk_elapsed_us(&clk, buffer, sizeof(buffer)), tag);
}

static void test_sequence_generators(int m, int n)
{
    time_sequence_generator("generate_sequence", m, n, generate_sequence);
    time_sequence_generator("sequence_withgoto", m, n, sequence_withgoto);
    time_sequence_generator("sequence_substitute", m, n, sequence_substitute);
}

int main(void)
{
    test_sequence_generators(2, 3);
    test_sequence_generators(5, 9);
    test_sequence_generators(4, 10);
    test_sequence_generators(6, 15);
    test_sequence_generators(7, 19);
    test_sequence_generators(8, 16);

    return 0;
}

编译命令行:

gcc -O3 -g -I./inc -std=c11 -Wall -Wextra -Werror  seq23.c -o seq23 -L./lib -lsoq 

标头安装在./inc中,而包含计时器代码的库在./lib中(在静态库libsoq.a中)。

在多次运行中,我得到的结果令人震惊且一致:

Number of sequences (m = 2, n = 3): 9 elapsed = 0.000005 (generate_sequence)
Number of sequences (m = 2, n = 3): 9 elapsed = 0.000000 (sequence_withgoto)
Number of sequences (m = 2, n = 3): 9 elapsed = 0.000000 (sequence_substitute)
Number of sequences (m = 5, n = 9): 59049 elapsed = 0.000098 (generate_sequence)
Number of sequences (m = 5, n = 9): 59049 elapsed = 0.000119 (sequence_withgoto)
Number of sequences (m = 5, n = 9): 59049 elapsed = 0.000068 (sequence_substitute)
Number of sequences (m = 4, n = 10): 10000 elapsed = 0.000012 (generate_sequence)
Number of sequences (m = 4, n = 10): 10000 elapsed = 0.000015 (sequence_withgoto)
Number of sequences (m = 4, n = 10): 10000 elapsed = 0.000010 (sequence_substitute)
Number of sequences (m = 6, n = 15): 11390625 elapsed = 0.013260 (generate_sequence)
Number of sequences (m = 6, n = 15): 11390625 elapsed = 0.015959 (sequence_withgoto)
Number of sequences (m = 6, n = 15): 11390625 elapsed = 0.010123 (sequence_substitute)
Number of sequences (m = 7, n = 19): 893871739 elapsed = 1.064473 (generate_sequence)
Number of sequences (m = 7, n = 19): 893871739 elapsed = 1.206680 (sequence_withgoto)
Number of sequences (m = 7, n = 19): 893871739 elapsed = 0.758287 (sequence_substitute)
Number of sequences (m = 8, n = 16): 0 elapsed = 4.819932 (generate_sequence)
Number of sequences (m = 8, n = 16): 0 elapsed = 5.712081 (sequence_withgoto)
Number of sequences (m = 8, n = 16): 0 elapsed = 3.705033 (sequence_substitute)

(m = 8, n = 16)序列生成16 8 = 2 32 序列,这意味着unsigned计数器溢出到0

令我惊讶的是sequence_withgoto()是最慢的功能;我的generate_sequence()居于中间,但问题规模最小。和问题中的sequence_substitute()最快,幅度明显(最后一个序列上使用goto的变体时间的大约2/3。

我在注释文件中使用以下算法实现了该算法:

/*
** Algorithm:
** Array data contains m values.
** The farthest right entry varies continuously; when it exceeds n-1, it
** is reset to 0.  If the increment wraps back to 0, then the previous
** index is incremented (and if it wraps to zero, ...).  The loop
** finishes when the zeroth index reaches n (wrapping is suppressed).
*/

摘要

要回答标题中的标题问题:

  • 否; goto语句不是强制性的。

要回答体内的速度问题:

  • 在大问题上,使用goto的代码通常不会更快(并且如果在小问题上具有任何性能优势,那么重要就不太可能了)。

答案 2 :(得分:1)

首先,计算结果数。这将是numbers_of_results = pow(n, m)。例如。如果m = 2n = 3,那么将有pow(3, 2) == 9个结果。

一旦知道有多少结果,就可以将其用于索引解决方案空间。例如:

    numbers_of_results = pow(n, m);
    for(index = 0; index < numbers_of_results; index++) {

    }

索引只是一个“基数m”数字,其中索引中的每个数字确定一个数字。要从索引中提取单个数字,可以使用digit = (index / pow(m, digit_number) ) % m,但是一次提取一个数字时可以避免使用pow()

例如:

unsigned int sequence(unsigned int m, unsigned int n) {
    unsigned int numbers_of_results, index, temp, digit_number;

    numbers_of_results = pow(n, m);
    for(index = 0; index < numbers_of_results; index++) {
        temp = index;
        for(digit_number = 0; digit_number < n; digit_number++) {
           digit = temp % m;
           temp /= m;
           printf("%d ", digit);
        }
        printf("/ ");
    }
    putchar('\n');
    return numbers_of_results;

现在;考虑一下“可读性/可维护性与性能”的折衷方案,以及(如果您根本不关心性能的话)您关心的CPU。

对于某些CPU(而非其他CPU),最好用自己的实现(仅针对整数设计)替换pow()。对于某些CPU(而非其他CPU),添加特殊情况版本可能会有所好处(例如,对于m是2的幂的所有情况,除法和取模可以用“以常数m移位”和“且常数为m-1的AND”)。对于某些CPU(而不是其他CPU),除法和模运算可能比分支预测错误的代价昂贵得多,因此将m舍入到最接近的2的幂可能会提高性能(因此您可以使用“ shift and AND”特殊版本),但随后取消/丢弃索引包含太大数字的情况。

答案 3 :(得分:0)

您所关注的与现实世界无关。不,我不是在骗你。我只是指出您担心的是无关紧要的事情。

最有效的实现是无效的实现。

实际上,代码的可维护性比绝对最大性能要重要得多。我们每天都会了解有关计算机科学的更多信息,并且我们会使用不断变化的硬件,因此,今天绝对最佳的明天就不会如此。

拥有足够好,并且易于修改/更新的价值,就很多。

生成序列时,速度并不重要,因为您只运行一次并保存结果。

使用序列时,通常无法存储和检索它们(因为Universe中没有足够的物质,或者I / O太慢而无法实用)。而是在序列中生成显式值This answer是我的一个相关问题,即序列中的值具有唯一数字的问题,它说明了如何执行此操作。本质上,您使用从0开始的无符号整数“索引”作为种子来生成序列中的值。

但是,有时候,您只是在玩代码,或者正在玩它(例如,代码溢出堆栈时在此处打高尔夫球)。或者,也许您有多种执行完全相同的操作的实现,并且希望公平地进行比较。

然后,问题出在微基准测试基准测试之间。

我们当前的计算机非常复杂,以至于考虑诸如 speed efficiency 之类的概念时,完全不同的操作会相互影响很大。最常见的机制是通过缓存。简而言之,如果您的超快功能使用大量缓存,则它可能会减慢其他操作的速度(因为不再需要缓存所需的数据,而必须从系统RAM加载它们),因此该功能的任务是一部分的功能使速度降低了

这意味着要进行适当的基准测试,对程序性能进行适当的分析,我们需要使用真实的有效负载。实际上,有几种不同的方法。并且只需尝试完整地了解每个实现的执行情况;当它们有效率时,它们缓慢而又耗费资源时,等等。

常见的问题是可扩展性。当有大量数据时,某些函数比其他函数要快,但是当数据集较小时,反函数可能成立。这在排序功能中尤其常见。如果没有太多数据,某些排序功能将非常慢;但是当您有足够的数据时,它比任何其他功能都快。 (对数值数据进行基数排序是一个完美的例子:它确实在线性时间内进行排序,但是线性因子是如此之大,以至于您需要具有数百万个条目的数组才能使基数排序比其他排序算法花费更少的时间;基数排序也倾向于比其他算法更重缓存。因此,即使在某些方面它是“高级”的,也不经常使用。)

在比较例如,我们使用术语 microbenchmark 。出于综合考虑,这些功能可以与人工测试用例一起使用,以提醒我们人们这种测试是指示性的,并且由于上述原因并不是完全可靠的。

微基准测试本身是一门正确做的艺术。

尤其是,仅对大量运行所花费的时间进行平均,并不能提供完整的信息。系统中其他地方无关的事件有时会减慢单个运行(微基准功能的运行);在最坏的情况下,您会认为这些是视频播放,鼠标移动或动画产生的“抖动”。由于这些事件,并且其他测量误差源具有相似的特性,因此所测量的时序具有不对称的误差线:所测量的持续时间“从不”过低,但通常过高,因为该时序涉及那些无关紧要的事件。 >

缓存效果意味着,如果对存储在同一缓冲区中的数据调用相同的函数,则第一次调用的速度将比后续调用慢,因为其定时将包括使高速缓存“热”所需的时间;从RAM加载内容。

由于这些原因,我建议使用中位数或其他百分位数代替平均值。

如果考虑到这一点,您对连续进行1000次调用可能花费多长时间几乎不感兴趣,因为您不会在实际程序中连续进行这些调用。但是,如果您知道每个函数调用花费的中位数时间是 T ,则知道在至少50%的微基准测试情况下,函数调用花费的时间是 T 或更短时间。

还有哪个时钟在计算机上使用的问题。我们可以测量挂钟时间(在POSIXy系统中为clock_gettime(CLOCK_MONOTONIC, &timespec)),或仅测量该特定线程占用的CPU时间(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &timespec))。挂钟时间受同一台计算机上运行的所有其他程序的影响,但更好地与人们对性能的期望相对应。对于不涉及大量数据的数学计算,CPU时间更好。 (我想说CPU时间对于OP想知道的功能来说更好。)

这件事的最后一个难题是编译器优化。如今,编译器可以在机器代码级别进行令人惊讶的优化,具体取决于调用函数的上下文。对于真正的基准测试,这不是问题,但是对于微基准测试,我们确实需要将功能分成单独的单元,以便我们可以进行比较。

(当然,所有结果都是特定于每台计算机的,并且会随所使用的编译器,编译器选项和库的不同而变化。仅仅因为微基准X在计算机A上比在计算机B上快,但这并不意味着微基准Y是在A上也比在B上更快。请记住:是指示性的,不是证明或确实可靠。)

现在,我对这堵墙感到无聊,我们可以看看实现一个真实的微基准来公平地比较此类序列生成器。

首先,I / O部分将成为瓶颈。我们把它扔掉。取而代之的是,我们使用本地缓冲区来保存字符串,并且每当新值完成时,我们都会调用回调函数。这样,编译器就不能做任何真正的坏事,就像完全避免计算该值一样,因为它必须假定使用了该值。

void sequence(int m, int n, void (*callback)(int index, char seq[]))
{
    int   index[m + 1];
    char  buffer[m + 1];
    int   count = 0;
    int   j = 0;

    buffer[m] = '\0';

A:
    if (j >= m) {
        callback(count, buffer);
        ++count;
        goto C;
    }
    index[j] = 0;

B:
    if (index[j] < n) {
        ++j;
        goto A;
    }

C:
    --j;
    if (j >= 0) {
        ++index[j];
        goto B;
    }

    return;
}

我还将i数组重命名为index。描述性名称效果更好。 假设我们将其放入 fiveseven.c 中。我们还写一个小的 Makefile 来帮助我们编译这些东西:

CC      := gcc
CFLAGS  := -Wall -O2
LDFLAGS :=
LIBS    := fiveseven.so

.PHONY: all clean

all: clean benchmark $(LIBS)

clean:
        rm -f benchmark $(LIBS)

%.so: %.c
        $(CC) $(CFLAGS) -fPIC -shared $^ -Wl,-soname,$@ $(LDFLAGS) -o $@

benchmark: benchmark.c
        $(CC) $(CFLAGS) $^ $(LDFLAGS) -ldl -o $@

请注意,缩进使用的是 Tab ,而不是空格;但是此论坛不允许使用粘贴的代码。因此,如果您复制粘贴以上内容,请运行sed -e 's|^ *|\t|' -i Makefile来修复缩进。

这将分别编译实际的微基准测试程序,从Benchmark.c到./benchmark;以及将要比较的一个或多个函数放入动态加载的库中。这样可以避免编译器对代码进行任何令人惊讶的优化。

如果添加其他实现,只需将其库名称(在Linux上以.so结尾)添加到LIBS行。请注意,您还可以运行make CC=clangmake LIBS=foo.so clean all等,在构建时覆盖变量。

当然,我们需要基准测试程序本身。这是一个实现,Benchmark.c:

#define _POSIX_C_SOURCE  200809L
// SPDX-License-Identifier: CC0-1.0

#include <stdlib.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <stdio.h>
#include <time.h>

typedef void (sequence_fn)(int, int, void (*)(int, char []));

static int  compare_int64(const void *ptr1, const void *ptr2)
{
    const int64_t  val1 = *(const uint64_t *)ptr1;
    const int64_t  val2 = *(const uint64_t *)ptr2;
    return (val1 < val2) ? -1 :
           (val1 > val2) ? +1 : 0;
}

static void  nothing(int index, char value[])
{
    return;
}

static double  median_cpu_time(const size_t  iterations,
                               const int     length,
                               const int     radix,
                               sequence_fn  *sequence_function)
{
    struct timespec  started, stopped;
    int64_t         *nanosecs;
    double           result;
    size_t           i;

    if (iterations < 1 || length < 1 || radix < 1 || !sequence_function)
        return -1.0; /* Invalid parameters. */

    nanosecs = malloc(iterations * sizeof nanosecs[0]);
    if (!nanosecs)
        return -1.0; /* Out of memory. */

    for (i = 0; i < iterations; i++) {

        clock_gettime(CLOCK_THREAD_CPUTIME_ID, &started);
        sequence_function(length, radix, nothing);
        clock_gettime(CLOCK_THREAD_CPUTIME_ID, &stopped);

        nanosecs[i] = (int64_t)(stopped.tv_sec - started.tv_sec) * INT64_C(1000000000)
                    + (int64_t)(stopped.tv_nsec - started.tv_nsec);
    }

    qsort(nanosecs, iterations, sizeof (int64_t), compare_int64);
    result = (double)nanosecs[iterations / 2] / 1000000000.0;
    free(nanosecs);
    return result;
}

int main(int argc, char *argv[])
{
    size_t       iterations;
    int          arg, length, radix;
    void        *handle;
    sequence_fn *func;    
    double       seconds;
    char         dummy;

    if (argc < 5) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help | help ]\n", argv[0]);
        fprintf(stderr, "       %s LENGTH RADIX ITERATIONS ./library.so ...\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "This program measures the median CPU time taken by\n");
        fprintf(stderr, "each sequence(LENGTH, RADIX, callback) call\n");
        fprintf(stderr, "over ITERATIONS iterations, as implemented in each\n");
        fprintf(stderr, "listed dynamic library.\n");
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    if (sscanf(argv[1], " %d %c", &length, &dummy) != 1 || length < 1) {
        fprintf(stderr, "%s: Invalid length.\n", argv[1]);
        return EXIT_FAILURE;
    }

    if (sscanf(argv[2], " %d %c", &radix, &dummy) != 1 || radix < 1) {
        fprintf(stderr, "%s: Invalid radix.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (sscanf(argv[3], " %zu %c", &iterations, &dummy) != 1 || iterations < 1) {
        fprintf(stderr, "%s: Invalid number of iterations.\n", argv[3]);
        return EXIT_FAILURE;
    }

    printf("Reporting median CPU time used over %zu iterations.\n", iterations);
    printf("Length = %d, radix = %d.\n", length, radix);
    fflush(stdout);
    for (arg = 4; arg < argc; arg++) {

        handle = dlopen(argv[arg], RTLD_NOW);
        if (!handle) {
            fprintf(stderr, "%s: %s.\n", argv[arg], dlerror());
            continue;
        }

        func = dlsym(handle, "sequence");
        if (!func) {
            fprintf(stderr, "%s: Library does not implement sequence().\n", argv[arg]);
            dlclose(handle);
            continue;
        }

        printf("    %s: ", argv[arg]);
        fflush(stdout);

        seconds = median_cpu_time(iterations, length, radix, func);

        printf("%.9f seconds median per call\n", seconds);
        fflush(stdout);

        dlclose(handle);
    } 

    return EXIT_SUCCESS;
}

请注意,它提供了一个不执行任何操作的回调函数,并且该程序的大部分实际上只是在提供命令行使用信息。在实践中这非常有用。我倾向于将每个“项目”放入单独的目录/文件夹。当我在寻找特定的东西时,我执行find + grep或grep -e pattern -R base-of-directories,然后检查可能的候选人。如果有可执行文件,我可以运行以查看其用法(我一直都有!),让我查看该特定目录的用途;读取数千行代码以尝试回忆是否正是我想要的那样,这既快捷又省力。

保存以上内容后,运行例如

make clean all
./benchmark 5 4 100000 ./fiveseven.so

查看每次调用OP的sequence()函数所花费的CPU时间(以秒为单位),包括为每个生成的值通过函数指针调用空操作的开销。

请注意,此微基准测试无法验证结果的正确性。这也是应该考虑的事情。