C++ 为什么BOOST_FOREACH不完全等同于手工编码的?
从 这导致了接近最优的代码生成;性能 BOOST_FOREACH通常在等效值的百分之几以内 手动编码回路 我想使用宏和非标准的typeof操作符,我们可以生成完全等价的一个。BOOST_FOREACH的哪些功能使其不精确 编辑: 我的版本:C++ 为什么BOOST_FOREACH不完全等同于手工编码的?,c++,boost-foreach,C++,Boost Foreach,从 这导致了接近最优的代码生成;性能 BOOST_FOREACH通常在等效值的百分之几以内 手动编码回路 我想使用宏和非标准的typeof操作符,我们可以生成完全等价的一个。BOOST_FOREACH的哪些功能使其不精确 编辑: 我的版本: #define EACH(it,v) \ for(typeof(v.begin()) it = v.begin();it != v.end(); ++it) //use this if you want a const_iterator
#define EACH(it,v) \
for(typeof(v.begin()) it = v.begin();it != v.end(); ++it)
//use this if you want a const_iterator from a non-const container
#define CONST_EACH(it,v) \
typedef typeof(v) v_type; \
typedef const v_type& const_type; \
for(typeof(static_cast<const_type>(v).begin()) it = static_cast<const_type>(v).begin(); it != static_cast<const_type>(v).end(); ++it)
#定义每个(it,v)\
for(typeof(v.begin())it=v.begin();it!=v.end();++it)
//如果希望从非常量容器中获得常量迭代器,请使用此选项
#定义每个常数(it,v)\
类型定义类型(v)v_类型\
typedef const v_type&const_type\
for(typeof(static_cast(v).begin())it=static_cast(v).begin();it!=static_cast(v.end();+it)
我试图写一个没有任何开销的版本。它使用非标准的typeof,并给出迭代器而不是value\u type。我遗漏了什么吗?我相信BOOST\u FOREACH为了支持自然循环语法而使用的一些技巧可能会生成多余的对象副本。BOOST FOREACH绝非无足轻重。根据gcc 4.6:
int main()
{
std::string hello( "Hello, world!" );
BOOST_FOREACH( char ch, hello )
{
std::cout << ch;
}
return 0;
}
有太多可能的事情你可能想循环。对于c++11,不再需要所有这些技巧,因为您可以使用
for(auto const &a: something){ .. }
或
如果手工编写循环代码,则可以利用迭代器和范围的已知(但不一定是编译器或boost_foreach)属性。所以你可能会做得更好
它还严重依赖于能够在编译时检测类的某些属性,如果不能(即编译器不能支持它使用的模板机制),则必须将其推迟到运行时。这显然不如手工编码(您可能刚刚知道)所得到的结果有效。为什么不问问您最喜欢的编译器 让我们使用一个简单的测试用例(以避免混乱): 这就简单地给出了:
define void @_Z9simpleforv() nounwind uwtable {
%1 = load i8** @HelloWorld, align 8, !tbaa !0 ; [#uses=3 type=i8*]
%2 = tail call i64 @strlen(i8* %1) nounwind readonly ; [#uses=2 type=i64]
%3 = getelementptr inbounds i8* %1, i64 %2 ; [#uses=1 type=i8*]
%4 = icmp eq i64 %2, 0 ; [#uses=1 type=i1]
br i1 %4, label %._crit_edge, label %.lr.ph
.lr.ph: ; preds = %.lr.ph, %0
%it.01 = phi i8* [ %7, %.lr.ph ], [ %1, %0 ] ; [#uses=2 type=i8*]
%5 = load i8* %it.01, align 1, !tbaa !1 ; [#uses=1 type=i8]
%6 = sext i8 %5 to i32 ; [#uses=1 type=i32]
%putchar = tail call i32 @putchar(i32 %6) nounwind ; [#uses=0 type=i32]
%7 = getelementptr inbounds i8* %it.01, i64 1 ; [#uses=2 type=i8*]
%8 = icmp eq i8* %7, %3 ; [#uses=1 type=i1]
br i1 %8, label %._crit_edge, label %.lr.ph
._crit_edge: ; preds = %.lr.ph, %0
ret void
}
对于BOOST\u FOREACH
:
; [#uses=0]
define void @_Z7foreachv() nounwind uwtable {
%1 = load i8** @HelloWorld, align 8, !tbaa !0 ; [#uses=1 type=i8*]
br label %2
; <label>:2 ; preds = %.preheader, %0
%.in = phi i8* [ %6, %.preheader ], [ %1, %0 ] ; [#uses=2 type=i8*]
%3 = load i8* %.in, align 1, !tbaa !1 ; [#uses=2 type=i8]
%4 = icmp eq i8 %3, 0 ; [#uses=1 type=i1]
br i1 %4, label %.critedge, label %.preheader
.preheader: ; preds = %2
%5 = sext i8 %3 to i32 ; [#uses=1 type=i32]
%putchar = tail call i32 @putchar(i32 %5) nounwind ; [#uses=0 type=i32]
%6 = getelementptr inbounds i8* %.in, i64 1 ; [#uses=1 type=i8*]
br label %2
.critedge: ; preds = %2
ret void
}
我认为这主要是因为别名的缘故。:当你使用(const)引用时,编译器很难发现某些变量没有别名,并且生成的代码也不太理想。顺便说一句,
typeof
操作符是不可移植的。你说的不可移植是什么意思?我知道这不是标准的一部分。我认为优化器可能已经赶上了这一说法。我对一些使用BOOST_FOREACH的代码进行了基准测试,看看是否可以使用等效的for循环来提高性能。BOOST_FOREACH代码稍微快一点(即,可能在误差范围内)@balki嗯,我的意思正是这样。不是所有的编译器都支持typeof
@balki,不,他们不能这样做。它不知道container.end()是否在循环过程中更改,甚至:.end()是否会产生副作用(如日志记录)。但是boost文档告诉用户.end()将被缓存,因此优化是可能的。用一个普通的循环就可以了!=v、 每次都会执行end(),您可以在那里编写任何类型的表达式,并且必须执行它。但是,在编译器设法解决如何处理模板之后,其中的大部分将以空代码结束。我知道这绝非小事。我的问题是为什么会这样?为什么没有任何开销就不能简单呢?那么,简单的代码应该是什么呢?有太多可能的事情你可能想循环。对于c++11,不再需要所有这些技巧。我已经更新了问题。是的,c++11是理想的,但我还不能移动。Boost foreach也做数组,还有一些我甚至不知道的特殊情况。此外,在boost中:在执行循环之前,将结束迭代器缓存在局部变量中。如果您建议使用另一种更快但同样灵活的foreach,那么请使您的代码完整且独立于编译器(包括typeof),并为几个示例计时。(如果您想对替代实现发表意见,我建议您提出一个新问题)。
#include <cstring>
#include <cstdio>
#include <boost/foreach.hpp>
char const* HelloWorld = "Hello, world!\n";
void simplefor() {
for(char const* it = HelloWorld, *end = HelloWorld + strlen(HelloWorld);
it != end;
++it)
{
printf("%c", *it);
}
}
void foreach() {
BOOST_FOREACH( char ch, HelloWorld )
{
printf("%c", ch);
}
}
~/projects$ clang++ -O2 -c -I/usr/lib/Boost/1-39-0-1/include/ test.cpp -emit-llvm
~/projects$ llvm-dis test.o -show-annotations
define void @_Z9simpleforv() nounwind uwtable {
%1 = load i8** @HelloWorld, align 8, !tbaa !0 ; [#uses=3 type=i8*]
%2 = tail call i64 @strlen(i8* %1) nounwind readonly ; [#uses=2 type=i64]
%3 = getelementptr inbounds i8* %1, i64 %2 ; [#uses=1 type=i8*]
%4 = icmp eq i64 %2, 0 ; [#uses=1 type=i1]
br i1 %4, label %._crit_edge, label %.lr.ph
.lr.ph: ; preds = %.lr.ph, %0
%it.01 = phi i8* [ %7, %.lr.ph ], [ %1, %0 ] ; [#uses=2 type=i8*]
%5 = load i8* %it.01, align 1, !tbaa !1 ; [#uses=1 type=i8]
%6 = sext i8 %5 to i32 ; [#uses=1 type=i32]
%putchar = tail call i32 @putchar(i32 %6) nounwind ; [#uses=0 type=i32]
%7 = getelementptr inbounds i8* %it.01, i64 1 ; [#uses=2 type=i8*]
%8 = icmp eq i8* %7, %3 ; [#uses=1 type=i1]
br i1 %8, label %._crit_edge, label %.lr.ph
._crit_edge: ; preds = %.lr.ph, %0
ret void
}
; [#uses=0]
define void @_Z7foreachv() nounwind uwtable {
%1 = load i8** @HelloWorld, align 8, !tbaa !0 ; [#uses=1 type=i8*]
br label %2
; <label>:2 ; preds = %.preheader, %0
%.in = phi i8* [ %6, %.preheader ], [ %1, %0 ] ; [#uses=2 type=i8*]
%3 = load i8* %.in, align 1, !tbaa !1 ; [#uses=2 type=i8]
%4 = icmp eq i8 %3, 0 ; [#uses=1 type=i1]
br i1 %4, label %.critedge, label %.preheader
.preheader: ; preds = %2
%5 = sext i8 %3 to i32 ; [#uses=1 type=i32]
%putchar = tail call i32 @putchar(i32 %5) nounwind ; [#uses=0 type=i32]
%6 = getelementptr inbounds i8* %.in, i64 1 ; [#uses=1 type=i8*]
br label %2
.critedge: ; preds = %2
ret void
}
void bestfor() {
for(char const ch: HelloWorld) {
printf("%c", ch);
}
}