编译时计算C ++ v.C 还要考虑tunes.org情况

问题描述

我知道constexpr关键字可用于在C ++中执行编译时计算。例如:

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

(摘自https://en.cppreference.com/w/cpp/language/constexpr

可以将编译时计算视为C ++ v。C的主要优势吗?

据我了解,在C语言中无法进行编译时计算。constexpr不可用,我认为必须在运行时对代码进行评估。

与同等的C程序相比,C ++程序是否可以通过这种方式获得更好的性能(例如速度)?

解决方法

只有一件事是确定的-编译时计算使C ++编译器变得更加复杂,而编译速度变得更慢,因为在编译期间需要编译器执行此操作;例如查看

constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main(void) {
    static_assert(factorial(10) == 3628800,"factorial 10 was correct");
    static_assert(factorial(3) == 42,"factorial 3 was 42");
}

由于后期 static_assert而不是编译必不可少。


C编译器不需要这种复杂性,因为不要求C编译器必须能够在编译期间计算递归函数的值。一个简单的C编译器可以很好地将每个 statement 分别汇编为机器代码,而不必记住前面的语句做了什么。 C标准当然不要求它能够在编译期间评估递归函数。

但这并不是说在编译期间没有C编译器会这样做。参见以下示例:

#include <stdio.h>

int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main(void) {
    printf("%d\n",factorial(10));
}

Compiled with GCC 10.2 as a C program with -O3,感谢the as-if rule,该程序成为了

factorial:
        mov     eax,1
        cmp     edi,1
        jle     .L4
.L3:
        mov     edx,edi
        sub     edi,1
        imul    eax,edx
        cmp     edi,1
        jne     .L3
        ret
.L4:
        ret
.LC0:
        .string "%d\n"
main:
        sub     rsp,8
        mov     esi,3628800
        mov     edi,OFFSET FLAT:.LC0
        xor     eax,eax
        call    printf
        xor     eax,eax
        add     rsp,8
        ret

更直接对应的

unsigned factorial(unsigned n) {
     unsigned i = 1;
     while (n > 1) {
         i *= n;
         n --;
     }
     return i;
}

int main(void) {
    printf("%d\n",3628800);
}

即编译器不仅将递归简化为一个简单的while循环,而且还解析了常量的阶乘,而且都没有任何特殊的关键字。

,

可以将编译时计算视为C ++ v。C的主要优势吗?

实际上,重要的不是编译,而是software building

请参阅build automation上的Wikipedia页面。

然后,请注意许多软件项目(包括githubgitlab上的许多开源项目)正在生成 C (甚至C ++)来自更抽象的内容的代码(例如,使用软件工具)。一个典型的例子显然是解析器生成器(也称为compiler-compilers),例如GNU bisonANTLR。另一个示例是粘合代码生成器,例如rpcgenSWIG。并且GNU autoconf使您的构建适应计算机上可用的软件包。请注意,Chicken-SchemeBigoo都从Scheme源代码生成C代码。当然请参见this。在某些情况下,巨大的C文件是通过微小的输入生成的(另请参见XBM格式)。 Maple能够生成大型C文件,并且在某些情况下会生成大量C代码-例如五百万行-很有道理(如Pitrat的书Artificial beings: the conscience of a conscious machine中所述)和blog

最后,whole-program optimization可以存在(请参见最新GCC中的Link-Time-Optimization-flto标志;您实际上将使用gcc -Wall -O2 -flto进行编译和link),并且在“链接时”需要一些编译器支持。

在某些情况下,编译时间并不那么重要(例如,编译FirefoxLinux kernelLibreOfficeGnomeGTK(从其源代码库中获取),但是构建时间可以持续数小时,或者甚至可以是数十分钟(因为有许多不同的translation units-具体来说,{{1} }或*.c文件-必须先进行编译然后链接)。

有传言称,Google内部会花费数小时的计算机时间来构建其大部分内部软件。

请注意,第一个C ++编译器(例如Cfront)已被实现为C代码生成器,并且大型软件(例如{{3 }}编译器有数十个专用的C或C ++代码生成器。尝试通过可用的源代码在笔记本电脑上构建针对您的GCC板的GCC交叉编译器(该板太小且功能不足,无法在其上直接编译GCC)。这样,在RaspBerryPi上的构建说明就有意义了。

有关生成C代码的C程序的示例,请参见我的LinuxFromScratch Linux版代码或manydl.c报告中描述的Bismon程序。过时的this draft项目的过去版本确实生成了一百万行C或C ++代码。 *.cc可以在几天内生成然后编译C代码,并说明GCC MELT可以被多次使用。有关在Linux上生成C ++的C ++软件的示例,请参见我的dlopen(3)项目。另请参阅RefPerSys,以获取与元编程和C或C ++代码生成有关的讨论。

还要考虑tunes.org情况

例如可能使用cross-compilation在笔记本电脑上为Arduino编译C代码或为RaspberryPi编译C ++代码。或在遥远的GCC超级计算机上编译您的PC代码。

关于C ++ top500 C

我对C ++标准versus的理解是,未在此处指定编译时计算(但我并不声称自己是C ++专家)。 特别是,没有什么禁止您编写C ++ n3337 (您可以在C,C ++,Ocaml,Java等语言中进行编码)。将该想法视为有趣的编程练习(但请在尝试之前先阅读interpreter)。

我的观点是,按照C ++标准中的规定,学习C ++的学生课堂可以被视为C ++的实现。教授C ++的一种好方法是向教室询问几个C ++程序的Dragon book,可以用铅笔,纸或semantics来教。我实际上以这种方式(在巴黎第六大学)教过whiteboard的一门课程。 黑板是黑色的,我用了各种颜色的粉笔。

还查看诸如operational semanticsFrama-C之类的软件工具。两者均为Clang static analyzer,因此您可以研究其来源。

与同等的C程序相比,C ++程序是否可以通过这种方式获得更好的性能(例如速度)?

表示您的意见,并且我不同意。是什么让您认为,如果open sourceOcaml的运行时是用C ++编写的,它将更快(您应该下载并研究源代码)? 一个有趣的练习可能是使用SBCL编译器以C ++进行编码(对于C语言,针对Linux上的x86 32位和x86-64位,以C语言进行编码),然后对所有改进进行基准测试。那个简单而又聪明的编译器可以非常快速地编译C代码,但是却很少tinyCC

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...