D中的表达式模板
目标是实现与中相同的效果:避免创建临时对象。我尝试过把C++示例翻译成D,但没有成功。我也尝试过不同的方法D中的表达式模板,d,template-meta-programming,D,Template Meta Programming,目标是实现与中相同的效果:避免创建临时对象。我尝试过把C++示例翻译成D,但没有成功。我也尝试过不同的方法 import std.datetime : benchmark; import std.stdio : writefln, writeln; void bench(alias fun, string time = "msecs")(string msg, uint n = 1_000_000) { auto b = benchmark!fun(n); writefln("
import std.datetime : benchmark;
import std.stdio : writefln, writeln;
void bench(alias fun, string time = "msecs")(string msg, uint n = 1_000_000) {
auto b = benchmark!fun(n);
writefln(" %s: %s ms", msg, b[0].to!(time, int));
}
alias double Real;
struct Expression(string op, E1, E2) {
E1 _v1;
E2 _v2;
alias _v1 v1;
alias _v2 v2;
auto opIndex(size_t i) {
return mixin("v1[i]" ~ op ~ "v2[i]");
}
auto opBinary(string op, E)(auto ref E e) {
return Expression!(op, typeof(this), E)(this, e);
}
}
struct ExpVector {
Real[40] _data = void;
alias _data this;
this(Real datum) pure nothrow { _data = datum; }
auto opBinary(string op, T)(auto ref T other) {
return Expression!(op, typeof(this), T)(this, other);
}
void opAssign(E)(auto ref E exp) {
foreach(i, ref datum; _data)
datum = exp[i];
}
}
struct Vector {
Real[40] _data = void;
alias _data this;
this(Real datum) pure nothrow { _data = datum; }
auto opBinary(string op)(auto ref Vector other) {
Vector ret;
foreach(i, datum; _data)
ret[i] = mixin("datum" ~ op ~ "other[i]");
return ret;
}
}
void main() {
ExpVector e1 = ExpVector(1.5);
ExpVector e2 = ExpVector(7.3);
ExpVector ef;
void T1() {
ef = (e1 + e2) * (e1 + e2);
}
bench!T1(" vector operations using template expression");
Vector v1 = Vector(1.5);
Vector v2 = Vector(7.3);
Vector vf;
void T2() {
vf = (v1 + v2) * (v1 + v2);
}
bench!T2(" regular vector operations");
void T3() {
for(int i = 0; i < vf.length; ++i)
vf[i] = (v1[i] + v2[i]) * (v1[i] + v2[i]);
}
bench!T3(" what is expected if template expressions worked and temporaries were not created.");
}
导入标准日期时间:基准;
导入标准stdio:writefln,writeln;
无效工作台(别名fun,字符串时间=“毫秒”)(字符串消息,uint n=1_000_000){
自动b=基准!乐趣(n);
writefln(“%s:%s ms”,msg,b[0]。到!(时间,int));
}
别名双实;
结构表达式(字符串op、E1、E2){
E1 v1;
E2-v2;
别名v1;
别名_v2;
自动opIndex(尺寸i){
返回mixin(“v1[i]”~op~“v2[i]”);
}
自动opBinary(字符串op,E)(自动引用E){
返回表达式!(op,typeof(this),E)(this,E);
}
}
结构扩展向量{
实际[40]_数据=无效;
别名_此数据;
这(真实数据)是纯粹的nothrow{u data=datum;}
自动opBinary(字符串op,T)(自动引用T其他){
返回表达式!(op,typeof(this),T)(this,other);
}
无效opAssign(E)(自动参考E exp){
foreach(i,参考基准;_数据)
基准=exp[i];
}
}
结构向量{
实际[40]_数据=无效;
别名_此数据;
这(真实数据)是纯粹的nothrow{u data=datum;}
自动操作二进制(字符串操作)(自动参考向量其他){
向量ret;
foreach(i,基准;_数据)
ret[i]=mixin(“数据”~op~“其他[i]”);
返回ret;
}
}
void main(){
ExpVector e1=ExpVector(1.5);
ExpVector e2=ExpVector(7.3);
ExpVector-ef;
void T1(){
ef=(e1+e2)*(e1+e2);
}
bench!T1(“使用模板表达式的向量操作”);
向量v1=向量(1.5);
向量v2=向量(7.3);
向量vf;
void T2(){
vf=(v1+v2)*(v1+v2);
}
bench!T2(“正则向量运算”);
void T3(){
对于(int i=0;i
表达式模板版本比非表达式模板版本慢。我希望表达式模板的版本更快,更接近预期。那么,我的表达式模板有什么问题?什么是正确的方式做表达式模板在D?< /P> < P>在维基百科的C++示例类中保存对临时的引用,并且在表达式的情况下…
Vec a,b;
双c;
向量计算结果=(a-b)*c
…在分配给结果向量之前,我们在内存中有一些树:
ab
\ /
\ /
\ /
矢量差
\
\
\
VecScaled(将“c”的副本直接嵌入到对象中)
VECKOLL只保留对A和B的引用,VCSCSCAP持有引用临时VECCORE(根据C++规则,直到表达式结束)才存活。最后,我们没有初始数据的重复,也没有不必要的计算(如果我们只使用向量的一些组成部分,而不是像简单地分配给向量那样全部使用)在结构表达式(字符串op、E1、E2)中,初始数据和中间数据仅复制到数据成员,在上一个表达式的末尾,您将看到
表情!(“*”,表达式!(“-”,向量,向量),双精度)
其中,外部表达式将具有内部表达式的副本(“-”,Vec,Vec)和双参数,而内部表达式将具有a和b向量的副本。因此,在一天结束时,您避免了临时Vec,而是复制了4份a和b(不包括结果向量的最终赋值)。
尽管如此,我不知道在这种情况下如何在D中避免复制。可能是指向结构的指针?(顺便说一句,c++11的自动类型推断和表达式模板引用了临时变量,但它们之间存在不幸的交互,这可能会导致错误:)好吧,甚至不考虑代码的作用,我会指出,你需要编译一个编译器,它与C++编译器共享一个后端,以便能够真正地比较语言而不是编译器实现。如果你使用C++的GDC来做C++的东西,你会得到更好的语言本身的比较。dmd代码可能会慢一些,因为dmd的后端优化不如gcc。可能还有更多,但是比较dmd和gcc可能只是比较两个编译器的优化器。我想你误解了我说的。我发布的代码的速度是没有表达式模板的版本的两倍多,也就是说,只有一个常规的
Vector
结构实现了opBinary
成员函数,两个版本都使用DMD编译。我不把它与维基百科中的C++代码进行比较,然后你应该发布两个版本。当你只有两个中的一个时,很难将某个东西与另一个东西进行比较。@JonathanMDavis我已经发布了完整的程序。你可以试试这个技巧,看看是否可以删除结构:我知道复制,但D不会让我把它们作为参考。我确实尝试过指针(我的印象是,指针会使我的代码变得不安全),它确实会产生很大的不同,但仍然不太一样。e、 例如,表达式模板版本需要250ms
,而150ms
是预期的。是的,指针使事情变得不安全。在我的答案的最后一页显示了这样的危险(关于C++,但是我认为我们可以用指针在D中使用表达式模板得到相同的结果)
Vec a, b;
double c;
Vec result = ( a - b ) * c
a b
\ /
\ /
\ /
VecDifference
\
\
\
VecScaled( has copy of 'c' embedded directly in object )
Expression!( "*", Expression!( "-", Vec, Vec ), double )