编译时计算(C++ 诉 C)
Compile-time computation (C++ v. C)
我了解到 constexpr
关键字可用于在 C++ 中执行编译时计算。例如:
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
(取自https://en.cppreference.com/w/cpp/language/constexpr)
可以将编译时计算视为 C++ 相对于 C 的关键优势吗?
据我所知,编译时计算在 C 中是不可能的。constexpr
不可用,我认为必须在运行时评估代码。
这是 C++ 程序与同等 C 程序相比可以获得更好性能(例如速度)的一种方式吗?
只有一件事是确定的 - compile-time 计算使 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 编译器可以很好地 assemble 每个 语句 分别为机器代码,而不必记住前面的语句做了什么。 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, and thanks to 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
循环,而且还解析了常量的阶乘,而且都没有任何特殊关键字。
Can compile-time computation be considered a key advantage of C++ v. C ?
其实重要的不是编译,而是software building.
请参阅 build automation 上的维基百科页面。
然后,请注意许多软件项目(包括github or gitlab) are generating C (or even C++) code from something more abstract (e.g. using software tools). A typical example is obviously parser generators (a.k.a. compiler-compilers) like GNU bison or ANTLR. Another example are glue code generators like rpcgen
or SWIG. And GNU autoconf adapt your build to the software packages available on your computer. Notice that both Chicken-Scheme and Bigoo are generating C code from Scheme source code. See of course this. In some cases huge C files are produced from tiny input (see also the XBM format). Maple is able to generate large C files, and there are cases where generating a lot of C code -e.g. half a million lines- makes sense (as explained in Pitrat's book Artificial beings: the conscience of a conscious machine) and blog上的许多开源项目。
最后,whole-program optimization can exist (see the -flto
flag in recent GCC for Link-Time-Optimization; you practically would compile and link 与 gcc -Wall -O2 -flto
) 并在“link-time”处需要一些编译器支持。
在某些情况下, 编译时间并不那么重要(想想编译 Firefox or the Linux kernel or LibreOffice or Gnome or GTK from its source code base), but the build time can last hours, or certainly dozens of minutes (because a lot of different translation units - 具体来说 *.c
或 *.cc
文件- 必须编译然后链接)。
Google 据传在内部花费数小时的计算机时间来构建他们的大部分内部软件。
请注意第一个 C++ 编译器(例如 Cfront) have been implemented as C code generators, and that a large software such as the GCC compiler has dozens of specialized C or C++ code generators. Try to build on your laptop from the available source code a GCC cross-compiler targeting your RaspBerryPi board (which is too small and underpowered to straight-compile GCC on it). Build instructions on LinuxFromScratch 然后是相关的。
有关 C 程序生成 C 代码的示例,请参阅我的 manydl.c code for Linux, or my Bismon program described in this draft report. Past versions of the obsolete GCC MELT project did generate a million lines of C or C++ code. manydl.c
is able to generate then compile C code during days, and illustrates that dlopen(3) can be used many times. For an example of a C++ software generating C++ on Linux, see my RefPerSys project. Look also into tunes.org 以了解与元编程和 C 或 C++ 代码生成相关的讨论。
还要考虑 cross-compilation 种情况
例如为 Arduino, or C++ code for your RaspberryPi on your laptop, perhaps with GCC. Or compiling on your PC code for a distant top500 超级计算机编译 C 代码。
关于 C++ versus C
我对C++标准的理解n3337是compile-time那里没有指定计算(但我不自称是C++专家).
特别是,没有什么可以禁止您在尝试之前先制作 C++ interpreter (you could code that in C, in C++, in Ocaml, in Java, etc...). Consider that idea as an interesting programming exercise (but read the Dragon book)。
我的观点是,按照该 C++ 标准中的规定,可以将学习 C++ 的学生课堂视为 C++ 实现。教授 C++ 的一个好方法是用这种方式向课堂询问 semantics of several C++ programs, and that can be taught with pencil and paper or a whiteboard. I actually taught a course on operational semantics(在巴黎第六大学)。
黑板是黑色的,我用了各种颜色的粉笔。
还可以查看 Frama-C or Clang static analyzer. Both are open source 等软件工具,这样您就可以研究它们的来源。
Is this this one manner in which a C++ programs can achieve better performance (e.g. speed) versus an equivalent C program ?
这是你的意见,我不同意。是什么让您认为 Ocaml or of SBCL would be faster (you should download and study the source code) if it has been written in C++ ? An interesting exercise could be to recode in C++ the tinyCC compiler (for C, targeting x86 32 bits and x86-64 bits on Linux, coded in C), and benchmark any improvement. That simple but clever compiler is compiling C code very quickly, but make too few compiler optimizations.
的运行时间
我了解到 constexpr
关键字可用于在 C++ 中执行编译时计算。例如:
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
(取自https://en.cppreference.com/w/cpp/language/constexpr)
可以将编译时计算视为 C++ 相对于 C 的关键优势吗?
据我所知,编译时计算在 C 中是不可能的。constexpr
不可用,我认为必须在运行时评估代码。
这是 C++ 程序与同等 C 程序相比可以获得更好性能(例如速度)的一种方式吗?
只有一件事是确定的 - compile-time 计算使 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 编译器可以很好地 assemble 每个 语句 分别为机器代码,而不必记住前面的语句做了什么。 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, and thanks to 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
循环,而且还解析了常量的阶乘,而且都没有任何特殊关键字。
Can compile-time computation be considered a key advantage of C++ v. C ?
其实重要的不是编译,而是software building.
请参阅 build automation 上的维基百科页面。
然后,请注意许多软件项目(包括github or gitlab) are generating C (or even C++) code from something more abstract (e.g. using software tools). A typical example is obviously parser generators (a.k.a. compiler-compilers) like GNU bison or ANTLR. Another example are glue code generators like rpcgen
or SWIG. And GNU autoconf adapt your build to the software packages available on your computer. Notice that both Chicken-Scheme and Bigoo are generating C code from Scheme source code. See of course this. In some cases huge C files are produced from tiny input (see also the XBM format). Maple is able to generate large C files, and there are cases where generating a lot of C code -e.g. half a million lines- makes sense (as explained in Pitrat's book Artificial beings: the conscience of a conscious machine) and blog上的许多开源项目。
最后,whole-program optimization can exist (see the -flto
flag in recent GCC for Link-Time-Optimization; you practically would compile and link 与 gcc -Wall -O2 -flto
) 并在“link-time”处需要一些编译器支持。
在某些情况下, 编译时间并不那么重要(想想编译 Firefox or the Linux kernel or LibreOffice or Gnome or GTK from its source code base), but the build time can last hours, or certainly dozens of minutes (because a lot of different translation units - 具体来说 *.c
或 *.cc
文件- 必须编译然后链接)。
Google 据传在内部花费数小时的计算机时间来构建他们的大部分内部软件。
请注意第一个 C++ 编译器(例如 Cfront) have been implemented as C code generators, and that a large software such as the GCC compiler has dozens of specialized C or C++ code generators. Try to build on your laptop from the available source code a GCC cross-compiler targeting your RaspBerryPi board (which is too small and underpowered to straight-compile GCC on it). Build instructions on LinuxFromScratch 然后是相关的。
有关 C 程序生成 C 代码的示例,请参阅我的 manydl.c code for Linux, or my Bismon program described in this draft report. Past versions of the obsolete GCC MELT project did generate a million lines of C or C++ code. manydl.c
is able to generate then compile C code during days, and illustrates that dlopen(3) can be used many times. For an example of a C++ software generating C++ on Linux, see my RefPerSys project. Look also into tunes.org 以了解与元编程和 C 或 C++ 代码生成相关的讨论。
还要考虑 cross-compilation 种情况
例如为 Arduino, or C++ code for your RaspberryPi on your laptop, perhaps with GCC. Or compiling on your PC code for a distant top500 超级计算机编译 C 代码。
关于 C++ versus C
我对C++标准的理解n3337是compile-time那里没有指定计算(但我不自称是C++专家). 特别是,没有什么可以禁止您在尝试之前先制作 C++ interpreter (you could code that in C, in C++, in Ocaml, in Java, etc...). Consider that idea as an interesting programming exercise (but read the Dragon book)。
我的观点是,按照该 C++ 标准中的规定,可以将学习 C++ 的学生课堂视为 C++ 实现。教授 C++ 的一个好方法是用这种方式向课堂询问 semantics of several C++ programs, and that can be taught with pencil and paper or a whiteboard. I actually taught a course on operational semantics(在巴黎第六大学)。 黑板是黑色的,我用了各种颜色的粉笔。
还可以查看 Frama-C or Clang static analyzer. Both are open source 等软件工具,这样您就可以研究它们的来源。
Is this this one manner in which a C++ programs can achieve better performance (e.g. speed) versus an equivalent C program ?
这是你的意见,我不同意。是什么让您认为 Ocaml or of SBCL would be faster (you should download and study the source code) if it has been written in C++ ? An interesting exercise could be to recode in C++ the tinyCC compiler (for C, targeting x86 32 bits and x86-64 bits on Linux, coded in C), and benchmark any improvement. That simple but clever compiler is compiling C code very quickly, but make too few compiler optimizations.
的运行时间