digitalmars.D - seeding the pot for 2.0 features
- BCS (45/45) Jan 23 2007 Now that the big 1.0 is out, I think that we should start considering wh...
- kris (9/69) Jan 23 2007 Hrm, I may be a dolt but the above looks like greek -- I have no idea,
- BCS (24/35) Jan 23 2007 The basic idea that spawned the suggestion was to have a template class ...
- kris (3/51) Jan 23 2007 Yeah, please forgive me for being a bit flippant; I really didn't get
- Pragma (43/69) Jan 23 2007 And a good one at that. But there are two gotchas here that deserve som...
- BCS (13/41) Jan 23 2007 I think non const would be better than non static, or was that a typo?
- Kristian Kilpi (13/18) Jan 25 2007 e(int =
- BCS (50/70) Jan 25 2007 I think that has been proposed but has some problems. For instance
- Kristian Kilpi (41/70) Jan 27 2007 t =
- Chad J (6/15) Jan 24 2007 ...
- BCS (18/21) Jan 26 2007 I think I have a better solution, let function statics behave like
- Mikola Lysenko (103/103) Jan 27 2007 I'll bite. Here are the two features I consider most important:
- Chad J (69/205) Jan 28 2007 Low dimensional primitive vectors would rock. I have to wonder though
- Mikola Lysenko (26/105) Jan 28 2007 Right. However, compare the syntax in the following two cases:
- Bill Baxter (46/113) Jan 28 2007 It's still a tiny fraction of the number of applications that use, say,
- Chad J (22/161) Jan 28 2007 What applications don't use vector instructions?
- Mikola Lysenko (6/12) Jan 28 2007 Actually, newer ARM devices support the NEON/VFP instruction set
- Bill Baxter (10/49) Jan 28 2007 Business applications? I'm not sure what you mean exactly. But the
- Mikola Lysenko (74/103) Jan 28 2007 This was the same sentiment I felt several years ago, but overtime I
- Joel C. Salomon (26/49) Jan 28 2007 Hmmm… D’s complex types seem to be syntactic sugar over a construct...
- Mikola Lysenko (33/37) Jan 29 2007 I can tell you right now that this won't work. I have tried using the
- Frits van Bommel (14/55) Jan 29 2007 Can GCC-like extended assembler (recently implemented in GDC:
- Mikola Lysenko (11/23) Jan 29 2007 For regular functions, I completely agree. The system should work at
- Walter Bright (3/5) Jan 29 2007 If you could write up a short proposal on exactly what operations should...
- Joel C. Salomon (18/27) Jan 30 2007 Sounds like what’s needed is in-line intermediate code rather than
- Frits van Bommel (13/28) Jan 30 2007 GCC (and recent GDC) already has such a mechanism, they call it
- Bill Baxter (5/15) Jan 29 2007 SIMD instructions can also be useful for complex operations, so I don't
- Bill Baxter (43/46) Jan 28 2007 "Vectors are more common than complex, and complex is built-in".
- Chad J (8/79) Jan 28 2007 Yeah I mentioned static arrays earlier, but I realized that they can be
- Bill Baxter (9/31) Jan 28 2007 Agreed. So basically the D 2.0 wish list item becomes to make float[4]
- Chad J (2/44) Jan 28 2007 *sigh* but I want my uber vector operations NOW! :)
- Mikola Lysenko (29/59) Jan 29 2007 I have never heard of any SIMD architecture where vectors works that
- Bill Baxter (11/40) Jan 29 2007 Ok. Well, I've never used any of these MMX/SSE/Altivec things myself,
- Chad J (15/66) Jan 29 2007 Seems it's great.
- Bill Baxter (18/49) Jan 29 2007 That could be. I seem to remember now the specific thing we were
- Bill Baxter (26/97) Jan 29 2007 Sorry to keep harping on this, but here's an article that basically says...
- Chad J (16/128) Jan 29 2007 That makes a lot of sense.
- Wolfgang Draxinger (38/38) Jan 27 2007 A quite cool feature was something similair to Python's
- Kirk McDonald (7/18) Jan 27 2007 Have you seen Pyd, I wonder?
- Wolfgang Draxinger (23/26) Jan 28 2007 Yes I have, but I was more thinking of using Python modules from
- Reiner Pope (47/60) Jan 28 2007 I think this could be a great feature, but be better as a template,
- Wolfgang Draxinger (6/9) Jan 29 2007 I wouldn't call it "opFakeMember", since those members are not
- Reiner Pope (27/27) Jan 28 2007 I think something like a 'super static' function could also be good
- Reiner Pope (7/43) Jan 28 2007 Alternatively, if this is too hard to implement (since I think it
- doob (59/76) Feb 05 2007 I think it would be good to add a "property" keyword and maybe also "get...
- BCS (14/18) Feb 05 2007 befor I'd go for that it rather see const aliases
Now that the big 1.0 is out, I think that we should start considering what to put into 2.0. I don't think we need to rush anything into D yet, but maybe we should start accumulating a list of things to consider. One overall thought I had is that it seems the big killer for C++ was to some extent that it is a strict superset of C. Maybe we should avoid this problem and not requiter that D2.0 be backwards compatible at the source level, but only at the ABI level sort of like C and D are now. Anyway, just a thought. Here are a few of my ideas for 2.0. 1) Modules structs and classes can have static this() functions, why functions? If a static nested constructor were allowed for functions, it would allow static function variable to be initialized at load time. 2) Why is there no tupleof property for interfaces? It would allow for some intriguing possibilities. interface I{...} class C : I { mixin ImpInterface!(I); } template ImpInterface(T) { mixin ImpInterface_!(T.tupleof); private ImpInterface_(F, V...) { mixin Implement!(F) static if(V.length > 0 mixin ImpInterface_!(V); } } be reel slick. template ImpInterface(T) { static foreach(F; T.tupleof); { F // assumes that F is a function // including name and everything { // implement F } } } 4) I guess that also needs a "implementable function alias". The syntax, not to mention the name, could ues a little work, but I expect you get the picture.
Jan 23 2007
BCS wrote:Now that the big 1.0 is out, I think that we should start considering what to put into 2.0. I don't think we need to rush anything into D yet, but maybe we should start accumulating a list of things to consider. One overall thought I had is that it seems the big killer for C++ was to some extent that it is a strict superset of C. Maybe we should avoid this problem and not requiter that D2.0 be backwards compatible at the source level, but only at the ABI level sort of like C and D are now. Anyway, just a thought. Here are a few of my ideas for 2.0. 1) Modules structs and classes can have static this() functions, why functions? If a static nested constructor were allowed for functions, it would allow static function variable to be initialized at load time. 2) Why is there no tupleof property for interfaces? It would allow for some intriguing possibilities. interface I{...} class C : I { mixin ImpInterface!(I); } template ImpInterface(T) { mixin ImpInterface_!(T.tupleof); private ImpInterface_(F, V...) { mixin Implement!(F) static if(V.length > 0 mixin ImpInterface_!(V); } } could be reel slick. template ImpInterface(T) { static foreach(F; T.tupleof); { F // assumes that F is a function // including name and everything { // implement F } } } 4) I guess that also needs a "implementable function alias". The syntax, not to mention the name, could ues a little work, but I expect you get the picture.Hrm, I may be a dolt but the above looks like greek -- I have no idea, at first glance, what the heck it does -- that's an important concern for the target audience, surely? Actually, what is the target audience for D? Specifically? Perhaps that's a good place to start :) Other than that, my suggestions for 2.0 would be to fix the problems in 1.0. The dreaded mixin noted above is a case in point. How about /removing/ that broken macro-expansion feature for 2.0 :D (somewhat tongue-in-cheek)
Jan 23 2007
Reply to kris,Hrm, I may be a dolt but the above looks like greek -- I have no idea, at first glance, what the heck it does -- that's an important concern for the target audience, surely? Actually, what is the target audience for D? Specifically? Perhaps that's a good place to start :) Other than that, my suggestions for 2.0 would be to fix the problems in 1.0. The dreaded mixin noted above is a case in point. How about /removing/ that broken macro-expansion feature for 2.0 :D (somewhat tongue-in-cheek)The basic idea that spawned the suggestion was to have a template class that implements an arbitrary interface and provides the functionality to transport it across a network connection. Given an interface. interface I { int foo(); } A server side program would do something like this. class C : I { int foo() { static int i = 0; return i++; } } ServeInterface!(I)(new C); and the client would do this: auto ref = new ConnectInterface!(I)("some.domain.name.org",port); writef("%d\n", ref.foo()); thus getting access to the C on the server. I have written a code generator to do this but is is vary painful to work with.
Jan 23 2007
BCS wrote:Reply to kris,Yeah, please forgive me for being a bit flippant; I really didn't getHrm, I may be a dolt but the above looks like greek -- I have no idea, at first glance, what the heck it does -- that's an important concern for the target audience, surely? Actually, what is the target audience for D? Specifically? Perhaps that's a good place to start :) Other than that, my suggestions for 2.0 would be to fix the problems in 1.0. The dreaded mixin noted above is a case in point. How about /removing/ that broken macro-expansion feature for 2.0 :D (somewhat tongue-in-cheek)The basic idea that spawned the suggestion was to have a template class that implements an arbitrary interface and provides the functionality to transport it across a network connection. Given an interface. interface I { int foo(); } A server side program would do something like this. class C : I { int foo() { static int i = 0; return i++; } } ServeInterface!(I)(new C); and the client would do this: auto ref = new ConnectInterface!(I)("some.domain.name.org",port); writef("%d\n", ref.foo()); thus getting access to the C on the server. I have written a code generator to do this but is is vary painful to work with.
Jan 23 2007
BCS wrote:Now that the big 1.0 is out, I think that we should start considering what to put into 2.0. I don't think we need to rush anything into D yet, but maybe we should start accumulating a list of things to consider. One overall thought I had is that it seems the big killer for C++ was to some extent that it is a strict superset of C. Maybe we should avoid this problem and not requiter that D2.0 be backwards compatible at the source level, but only at the ABI level sort of like C and D are now. Anyway, just a thought.And a good one at that. But there are two gotchas here that deserve some discussion. First - You need some way to flag to a compiler which D version is being used. 0) compiler arg that forces one version or the other for all input files (obvious) 1) use a different file extension: ".d2" 2) provide a pragma: "pragma(dversion,2); 3) abuse the version statement: "version dlanguage = 2;" 4) all of the above 5) something else Second - If the new D stuff were strictly a superset, in terms of syntax and behavior, then only new constructs would fail on older compilers; not the other way around.Here are a few of my ideas for 2.0. 1) Modules structs and classes can have static this() functions, why functions? If a static nested constructor were allowed for functions, it would allow static function variable to be initialized at load time.Like this? void foobar(){ static uint baz; static void this(){ baz = 111; } } IMO, this doesn't seem all that useful. I would much rather have D allow non-static expressions for static initializers; which would help cover this case and several others. Were the evaluation of delegates made to implicitly call on conversion to the lvalue's type, then this would work: void foobar(){ static uint baz = { return 111; }; // static anon delegate w/implicit call }2) Why is there no tupleof property for interfaces? It would allow for some intriguing possibilities.Agreed. IMO, you should be able to tupleof() *anything* and get a bag of types that describe the target's type in detail. If this is the route D is going to take for reflection, then it must be more expressive.could be reel slick.Yes. While we can get by utilizing recursion for iteration, it makes some idioms a major PITA. Honestly, this is how I interpreted Walter's first demonstration of foreach() with tuples the first time around. It wasn't until I started using tuples that I realized that it was non-static.4) I guess that also needs a "implementable function alias". The syntax, not to mention the name, could ues a little work, but I expect you get the picture.You lost me here. Care to elaborate? :) 5) I'll contribute one more: a "true closure" syntax. I really don't care keyword magic is used to make this work, just as long as it's less verbose than creating a class w/a single method to simulate the same thing. //old and busted: int delegate() foobar(int c){ return { return c; }; // delegate references current stack frame - this is worse than broken. } //new hotness: int delegate() foobar(int c){ return final{ return c; }; // delegate is propped up by copy of the context that is on the heap } It doesn't need to use 'final' per-se - any keyword that isn't confusing will do (like 'alias'). The problem, as you can imagine, is a pain to implement and has some non-trival side-effects of its own (like aliasing a stack frame that has inout parameters). This really needs some discussion. -- - EricAnderton at yahoo
Jan 23 2007
Reply to Pragma,First - You need some way to flag to a compiler which D version is being used. 0) compiler arg that forces one version or the other for all input files (obvious) 1) use a different file extension: ".d2" 2) provide a pragma: "pragma(dversion,2); 3) abuse the version statement: "version dlanguage = 2;" 4) all of the above 5) something elsemagic! {try parser(1.0, file); catch paser(2.0,file);}void foobar(){ static uint baz; static void this(){ baz = 111; } } IMO, this doesn't seem all that useful. I would much rather have D allow non-static expressions for static initializers;I think non const would be better than non static, or was that a typo?From the example I gave, the foreach would iterate over the members of I.tupleof and generate a function for each method that "I" defines. Take a look at my reply to kris for an example of what this would be used for. (short vertion: network transport of an interface)4) I guess that also needs a "implementable function alias". The syntax, not to mention the name, could ues a little work, but I expect you get the picture.You lost me here. Care to elaborate? :)5) I'll contribute one more: a "true closure" syntax. I really don't care keyword magic is used to make this work, just as long as it's less verbose than creating a class w/a single method to simulate the same thing.yah. my choice: dynamic init structs and explicit context for delegates int i,j,k; // pickle i,j,k then use them auto (new struct {int i_ = i; int j_ = j; int k_ = k; }).delegate(int m){return ((i_ * m)+j_)*m +k_;}
Jan 23 2007
On Wed, 24 Jan 2007 02:09:09 +0200, BCS <ao pathlink.com> wrote: [snip]my choice: dynamic init structs and explicit context for delegates int i,j,k; // pickle i,j,k then use them auto (new struct {int i_ =3D i; int j_ =3D j; int k_ =3D k; }).delegat=e(int =m){return ((i_ * m)+j_)*m +k_;}While we are at it, why not just let the compiler to generate context fo= r = delegates? ;) E.g. int i, j; return int delegete(int m) {return i * m + j;} -> return int (new struct {int i_ =3D i; int j_ =3D j}).delegete(int m) = {return = i_ * m + j_;}
Jan 25 2007
Kristian Kilpi wrote:On Wed, 24 Jan 2007 02:09:09 +0200, BCS <ao pathlink.com> wrote: [snip]I think that has been proposed but has some problems. For instance auto dg = int delegate(int m) {return i * m + j;} fnUsingDg(dg); // doesn't need context i++; j++; // did that effect the context? if(boolFn()) return dg; // now we need context else // or do we?? return int delegate(int m) {return i + j;} the explicit form has some advantages in clarity maybe a cut down version auto {i; j; k;}.dup.delegate(int m) { return ((i_ * m)+j_)*m +k_; } going back to my other idea (the one involving implementing an interface from its tuple) you might be able to build a template that builds and instance a struct BuildContext(alias A...){ ... } int i,j,k; return BuildContext!(i,j,k).delegate() { return i+j+k; // refers to members of context not function }; I'm not quite sure how to make it work under the hood but it would involve having aliases tuples that can act as: a type list to build structs a name list to name struct members a type/argument list to form a function a symbol list to access the symbols instancing the templates BuildContext!(alias A...)() { auto ret = new struct { // same types, same symbols A.types A.symbols; void set(A.types val) { // copy args *this = val; } } // call using whatever was used to instance the template ret.set(A.values); return ret; } Ahh, Errr. That would be a trick, and is asking for quite a lot.my choice: dynamic init structs and explicit context for delegates int i,j,k; // pickle i,j,k then use them auto (new struct {int i_ = i; int j_ = j; int k_ = k; }).delegate(int m){return ((i_ * m)+j_)*m +k_;}While we are at it, why not just let the compiler to generate context for delegates? ;) E.g. int i, j; return int delegete(int m) {return i * m + j;} -> return int (new struct {int i_ = i; int j_ = j}).delegete(int m) {return i_ * m + j_;}
Jan 25 2007
On Thu, 25 Jan 2007 20:08:29 +0200, BCS <BCS pathlink.com> wrote:Kristian Kilpi wrote:ate(int =On Wed, 24 Jan 2007 02:09:09 +0200, BCS <ao pathlink.com> wrote: [snip]my choice: dynamic init structs and explicit context for delegates int i,j,k; // pickle i,j,k then use them auto (new struct {int i_ =3D i; int j_ =3D j; int k_ =3D k; }).deleg=t =m){return ((i_ * m)+j_)*m +k_;}While we are at it, why not just let the compiler to generate contex=) =for delegates? ;) E.g. int i, j; return int delegete(int m) {return i * m + j;} -> return int (new struct {int i_ =3D i; int j_ =3D j}).delegete(int m=[snip] Hmm, yep, that's problematic. First, I tried to come up with some simple rules, For example, the conte= xt = should be automatically generated when: 1) A delegate is returned. 2) A delegate is assigned to something that is not a local variable. A class object is considered to be a local when 'scope' is used wi= th = it. But, nothing is ever so simple, isn't it? ;) They (the rules) would like= ly = cause *way* more trouble than good. However, it would be nice if one could tell the compiler to automaticall= y = create the context for a delegate (probably your explicit syntax should = be = possible too). For example, *something* like this: dg.createContext(); //compiler generates the context Hmm, maybe the compiler should automatically generate context for = anonymous delegates, after all, when they are returned from a function: int i, j; return delegate int(int m) {return i + j;}; //creates context But not when it's returned via a variable: int i, j; auto dg =3D delegate int(int m) {return i + j;}; return dg; //no context is created You would have to call 'createContext()' by yourself. Preventing context from being generated should also be possible, of cour= se: return (delegate int(int m) {return i + j;}).noContext(); //don't = create context I think implicit context for anonymous delegates returned from functions= = would reduce the number of errors people will make with them. But it's a= n = exception to the rules, which is never good.{return i_ * m + j_;}I think that has been proposed but has some problems. For instance auto dg =3D int delegate(int m) {return i * m + j;} fnUsingDg(dg); // doesn't need context i++; j++; // did that effect the context? if(boolFn()) return dg; // now we need context else // or do we?? return int delegate(int m) {return i + j;}
Jan 27 2007
BCS wrote:Now that the big 1.0 is out, I think that we should start considering what to put into 2.0. I don't think we need to rush anything into D yet, but maybe we should start accumulating a list of things to consider. One overall thought I had is that it seems the big killer for C++ was to some extent that it is a strict superset of C. Maybe we should avoid this problem and not requiter that D2.0 be backwards compatible at the source level, but only at the ABI level sort of like C and D are now. Anyway, just a thought.... The first thing that comes to my mind is explicit properties. I believe they can be done without using new keywords or sacrificing much generality. Eventually the implicit properties could be deprecated, when everyone feels they are no longer widely used.
Jan 24 2007
BCS wrote:1) Modules structs and classes can have static this() functions, why functions? If a static nested constructor were allowed for functions, it would allow static function variable to be initialized at load time.I think I have a better solution, let function statics behave like globals as far as access goes (plus default to private if that isn't already the case). int[int] k; void fn() { static int[int] i; ... } static this() { foreach(int[2] j; [cast(int[2]) [1,0],[2,1],[4,2],[8,3],...]) { k[j[1]] = j[0]; fn.i[j[0]] = j[1]; } }
Jan 26 2007
I'll bite. Here are the two features I consider most important: 1. Dynamic Closures: See: http://lists.puremagic.com/pipermail/digitalmars-d/2006-August/007520.html 2. Low dimensional vectors as primitive types Specifically I would like to see the types int, real, float etc. extended into 2-4 dimensional vectors. ie. int2, real4, float3. This would be exceptionally useful in many applications which require coordinate geometry. Here is a very brief list: Scientific Programs Physics Simulation Computer Graphics Video Games User Interfaces Computational Geometry Robotics Fluid Simulation etc. I would prefer not to recount the number of times I have written my own vector library, and how tedious they are to create. For most every language I learn it is the first thing I need to write, since so few are willing to provide a default implementation. In my opinion, this is unacceptable for a problem which occurs so frequently. One option is to extend the standard library with a vector-types class, but this is not nearly as nice a compiler level implementation. 1. The 90 degrees rotation trick This is based on the following article: http://www.flipcode.com/articles/article_fastervectormath.shtml The basic idea is we use templates to expand vector expressions to achieve better register use and memory locality. Here is a simple example: a = b + c * d; Normally, we would translate this as something like: a = b.opAdd(c.opMul(d)); Which gives the following assembler: //Do opMul mov reg, c.x mul reg, d.x mov tmp.x, reg mov reg, c.y mul reg, d.y mov tmp.y, reg mov reg, c.z mul reg, d.z mov tmp.z, reg //Do opAdd mov reg, tmp.x add reg, b.x mov a.x, reg mov reg, tmp.y add reg, b.y mov a.y, reg mov reg, tmp.z add reg, b.z mov a.z, reg This is not particularly efficient, since it requires the creation of a temporary vector to store the result from c * d. A better strategy involves 'rotating the computation 90 degrees' and performing the expression on a per-component level: //x - component mov reg, c.x mul reg, d.x add reg, b.x mov a.x, reg //y - component mov reg, c.y mul reg, d.y add reg, b.y mov a.y, reg //z - component mov reg, c.z mul reg, d.z add reg, b.z mov a.z, reg The performance improvement becomes more substantial the longer the expression. Since overloaded operators do not instantiate templates, there is no obvious way to obtain this result in the current language spec. 2. Architecture specific optimizations (SIMD) For low dimensional arithmetic, many architectures provide specially optimized instruction for low dimensional vectors. The problem is most languages do not exploit them. Creating efficient SIMD code is impossible for a library, since each opAdd/opMul must be written using inline assembler and therefore incurs the overhead of a function call regardless. This is worsened by the fact that moving to/from a vector register is typically very expensive. A compiler level implementation can easily avoid these issues by assigning vector expressions to a register when passing them. Moreover it is more portable than compiler intrinsics like MSVC's SSE extensions. The implementation can easily emit fallback code if the architecture does not support SIMD instructions. 3. Swizzles A swizzle is a reordering of the elements in a vector. Shader languages like Cg or GLSL typically support them, given their utility in certain types of computations. Here are some examples: v.xxxx // Returns a vector with v.x broadcast to all components v.xyz // Returns only the xyz components of v v.zyx // Returns a vector consisting of the reverse of v's xyz components Enumerating all possible swizzles within a template is impossible, and therefore requires one function per swizzle. The result is massive code bloat, and many lines of automatically generated gibberish. To get an idea at how many functions this requires, the total number of swizzles for 2-4 component vectors is 4^4 + 4^3 + 4^2 + 4 or 340. Multiply that by the number of primitive types and the result becomes quite large. -Mik
Jan 27 2007
Mikola Lysenko wrote:I'll bite. Here are the two features I consider most important: 1. Dynamic Closures: See: http://lists.puremagic.com/pipermail/digitalmars-d/2006-August/007520.html 2. Low dimensional vectors as primitive types Specifically I would like to see the types int, real, float etc. extended into 2-4 dimensional vectors. ie. int2, real4, float3. This would be exceptionally useful in many applications which require coordinate geometry. Here is a very brief list: Scientific Programs Physics Simulation Computer Graphics Video Games User Interfaces Computational Geometry Robotics Fluid Simulation etc. I would prefer not to recount the number of times I have written my own vector library, and how tedious they are to create. For most every language I learn it is the first thing I need to write, since so few are willing to provide a default implementation. In my opinion, this is unacceptable for a problem which occurs so frequently. One option is to extend the standard library with a vector-types class, but this is not nearly as nice a compiler level implementation. 1. The 90 degrees rotation trick This is based on the following article: http://www.flipcode.com/articles/article_fastervectormath.shtml The basic idea is we use templates to expand vector expressions to achieve better register use and memory locality. Here is a simple example: a = b + c * d; Normally, we would translate this as something like: a = b.opAdd(c.opMul(d)); Which gives the following assembler: //Do opMul mov reg, c.x mul reg, d.x mov tmp.x, reg mov reg, c.y mul reg, d.y mov tmp.y, reg mov reg, c.z mul reg, d.z mov tmp.z, reg //Do opAdd mov reg, tmp.x add reg, b.x mov a.x, reg mov reg, tmp.y add reg, b.y mov a.y, reg mov reg, tmp.z add reg, b.z mov a.z, reg This is not particularly efficient, since it requires the creation of a temporary vector to store the result from c * d. A better strategy involves 'rotating the computation 90 degrees' and performing the expression on a per-component level: //x - component mov reg, c.x mul reg, d.x add reg, b.x mov a.x, reg //y - component mov reg, c.y mul reg, d.y add reg, b.y mov a.y, reg //z - component mov reg, c.z mul reg, d.z add reg, b.z mov a.z, reg The performance improvement becomes more substantial the longer the expression. Since overloaded operators do not instantiate templates, there is no obvious way to obtain this result in the current language spec. 2. Architecture specific optimizations (SIMD) For low dimensional arithmetic, many architectures provide specially optimized instruction for low dimensional vectors. The problem is most languages do not exploit them. Creating efficient SIMD code is impossible for a library, since each opAdd/opMul must be written using inline assembler and therefore incurs the overhead of a function call regardless. This is worsened by the fact that moving to/from a vector register is typically very expensive. A compiler level implementation can easily avoid these issues by assigning vector expressions to a register when passing them. Moreover it is more portable than compiler intrinsics like MSVC's SSE extensions. The implementation can easily emit fallback code if the architecture does not support SIMD instructions. 3. Swizzles A swizzle is a reordering of the elements in a vector. Shader languages like Cg or GLSL typically support them, given their utility in certain types of computations. Here are some examples: v.xxxx // Returns a vector with v.x broadcast to all components v.xyz // Returns only the xyz components of v v.zyx // Returns a vector consisting of the reverse of v's xyz components Enumerating all possible swizzles within a template is impossible, and therefore requires one function per swizzle. The result is massive code bloat, and many lines of automatically generated gibberish. To get an idea at how many functions this requires, the total number of swizzles for 2-4 component vectors is 4^4 + 4^3 + 4^2 + 4 or 340. Multiply that by the number of primitive types and the result becomes quite large. -MikLow dimensional primitive vectors would rock. I have to wonder though if array operations would do the trick instead though, especially with static arrays. uint4 foo; uint4 bar = someDefaultValue; foo.x = x; foo.y = y; foo.z = z; uint4 result = foo + bar; becomes uint[4] foo; uint[4] bar = someDefaultValue; foo[0] = x; foo[1] = y; foo[2] = z; uint[4] result = foo + bar; Maybe not as readable, depending on the context, but that should suffice, right? The only trick then is to make sure the compiler recognizes these cases and optimizes for them. For example large dynamic arrays which are worthy of some fixed overhead before jumping into simd instructions or whatever, whereas the possibility of fixed overhead may change the way these small static things get optimized. Point is, maybe this is just a quality of implementation issue once array operations are added? Swizzling not templatable, really? ... import std.stdio; import std.traits; template doAssignment(dchar[] ordering, uint index = 0) { static if ( index < ordering.length ) { // dummy will get optimized away by dmd auto dummy = temp[index] = vector[cast(uint)ordering[index]]; mixin doAssignment!(ordering, index + 1); } } void swizzle(T, dchar[] ordering)(T vector) { T temp; static if ( !isStaticArray!(T) ) temp.length = vector.length; mixin doAssignment!(ordering); vector[0..ordering.length] = temp[0..ordering.length]; static if ( !isStaticArray!(T) ) delete temp; } void main() { int[4] point = [7,73,42,5]; writefln( point ); // prints [7,73,42,5] swizzle!(int[],[3,1,2,0])( point ); writefln( point ); // prints [5,73,42,7] real[] array = [81345.536,5.67,43.351,0.0,932.4,0.03,0.9852,57]; writefln( array ); swizzle!(real[],[0,1,2,4,3,5])(array); writefln( array ); } Now this is suboptimal, but I have made an effort to get all of the ordering values at compile time. Now we can hand those to a template and have it return information about optimal order of assignment and temporary usage. Now when you need to use said information, it should be doable either by clever writing of the assignment operations or by pasting together stuff using mixins. As for the fact that swizzle is a function, well it will probably get inlined, and if it doesn't, it should (quality of implementation issue).
Jan 28 2007
Chad J wrote:Low dimensional primitive vectors would rock. I have to wonder though if array operations would do the trick instead though, especially with static arrays. uint4 foo; uint4 bar = someDefaultValue; foo.x = x; foo.y = y; foo.z = z; uint4 result = foo + bar; becomes uint[4] foo; uint[4] bar = someDefaultValue; foo[0] = x; foo[1] = y; foo[2] = z; uint[4] result = foo + bar; Maybe not as readable, depending on the context, but that should suffice, right? The only trick then is to make sure the compiler recognizes these cases and optimizes for them. For example large dynamic arrays which are worthy of some fixed overhead before jumping into simd instructions or whatever, whereas the possibility of fixed overhead may change the way these small static things get optimized. Point is, maybe this is just a quality of implementation issue once array operations are added? Swizzling not templatable, really? ... import std.stdio; import std.traits; template doAssignment(dchar[] ordering, uint index = 0) { static if ( index < ordering.length ) { // dummy will get optimized away by dmd auto dummy = temp[index] = vector[cast(uint)ordering[index]]; mixin doAssignment!(ordering, index + 1); } } void swizzle(T, dchar[] ordering)(T vector) { T temp; static if ( !isStaticArray!(T) ) temp.length = vector.length; mixin doAssignment!(ordering); vector[0..ordering.length] = temp[0..ordering.length]; static if ( !isStaticArray!(T) ) delete temp; } void main() { int[4] point = [7,73,42,5]; writefln( point ); // prints [7,73,42,5] swizzle!(int[],[3,1,2,0])( point ); writefln( point ); // prints [5,73,42,7] real[] array = [81345.536,5.67,43.351,0.0,932.4,0.03,0.9852,57]; writefln( array ); swizzle!(real[],[0,1,2,4,3,5])(array); writefln( array ); } Now this is suboptimal, but I have made an effort to get all of the ordering values at compile time. Now we can hand those to a template and have it return information about optimal order of assignment and temporary usage. Now when you need to use said information, it should be doable either by clever writing of the assignment operations or by pasting together stuff using mixins. As for the fact that swizzle is a function, well it will probably get inlined, and if it doesn't, it should (quality of implementation issue).Right. However, compare the syntax in the following two cases: real4 a, b; a = b.wzyx versus: real[4] a, b; a = swizzle!(real[], [3, 2, 1, 0])(b); There is also a chance that the compiler may miss inlining the swizzle, resulting in code bloat, (as you pointed out.) Add in the fact that a compiler can exploit instructions like SSE's pshuf, and it becomes pretty clear that for low-d vectors a compiler level implementation is superior. On the topic of using array operations as a replacement for low-dimension vectors, I still have some mixed feelings about it. I think higher dimensional array ops are of dubious value, and vaguely reminiscent of APL. For most applications, the higher dimension array operations are overkill, and they will inevitably commit horrible acts of obfuscation: bool stricmp(char[] str1, char[] str2) { return ((str1 ^ str2) & ~('a' - 'A')) == 0; } Faced with such monstrosities, it might be best to keep vector code in the lower dimensions where it is most strongly connected to its geometric meaning. -Mik
Jan 28 2007
Mikola Lysenko wrote:I'll bite. Here are the two features I consider most important: 2. Low dimensional vectors as primitive types Specifically I would like to see the types int, real, float etc. extended into 2-4 dimensional vectors. ie. int2, real4, float3. This would be exceptionally useful in many applications which require coordinate geometry. Here is a very brief list: Scientific Programs Physics Simulation Computer Graphics Video Games User Interfaces Computational Geometry Robotics Fluid SimulationIt's still a tiny fraction of the number of applications that use, say, strings. So the ubiquity argument for inclusion is pretty weak, I think.etc. I would prefer not to recount the number of times I have written my own vector library, and how tedious they are to create. For most every language I learn it is the first thing I need to write, since so few are willing to provide a default implementation. In my opinion, this is unacceptable for a problem which occurs so frequently.Again, it occurs for *you* frequently (and I'll admit for me too), but still the vast majority of programmers out there have never had a burning need for a float3 with all the bells and whistles. If the need for a vector library were truly ubiquitous, it seems like it would be easier to find a decent implementation on the web, or that one would at least be available in the standard library of the given programming language. As far as D is concerned, Helix has a pretty decent implementation. See http://www.dsource.org/projects/helix. It lacks Vector2's but I've added them to my own copy and I'd be happy to send it to you if you like.One option is to extend the standard library with a vector-types class, but this is not nearly as nice a compiler level implementation.I'm not convinced that a compiler-level implementation of these things is necessary.1. The 90 degrees rotation trick This is based on the following article: http://www.flipcode.com/articles/article_fastervectormath.shtml ... The performance improvement becomes more substantial the longer the expression. Since overloaded operators do not instantiate templates, there is no obvious way to obtain this result in the current language spec.I thought the new opAssign was supposed to be enough to make expression templates work in D. Don Clugston even posted a proof-of-concept that would use templates to rearrange expressions a while back. Anyway, for this one, I think preferred approach is to make the core language expressive enough so that tricks like expression templates can work, rather than implementing such optimizations for particular cases in the compiler.2. Architecture specific optimizations (SIMD) For low dimensional arithmetic, many architectures provide specially optimized instruction for low dimensional vectors. The problem is most languages do not exploit them. Creating efficient SIMD code is impossible for a library, since each opAdd/opMul must be written using inline assembler and therefore incurs the overhead of a function call regardless. This is worsened by the fact that moving to/from a vector register is typically very expensive. A compiler level implementation can easily avoid these issues by assigning vector expressions to a register when passing them. Moreover it is more portable than compiler intrinsics like MSVC's SSE extensions. The implementation can easily emit fallback code if the architecture does not support SIMD instructions.Again, this sounds like it would be better to solve the generic issue of libraries not being able to take maximum advantage of existing hardware optimizations, like the issue with ASM methods not being inline-able.3. Swizzles A swizzle is a reordering of the elements in a vector. Shader languages like Cg or GLSL typically support them, given their utility in certain types of computations. Here are some examples: v.xxxx // Returns a vector with v.x broadcast to all components v.xyz // Returns only the xyz components of v v.zyx // Returns a vector consisting of the reverse of v's xyz components Enumerating all possible swizzles within a template is impossible, and therefore requires one function per swizzle. The result is massive code bloat, and many lines of automatically generated gibberish. To get an idea at how many functions this requires, the total number of swizzles for 2-4 component vectors is 4^4 + 4^3 + 4^2 + 4 or 340. Multiply that by the number of primitive types and the result becomes quite large.Are swizzles all that useful outside of Shader languages? Part of the reason they are useful in shaders is that GPU's can do a swizzles for free. Can CPUs (I dunno)? Another part of the reason is that all operations happen on 4-components no matter what, so if you want to multiply a scalar inside a vector times another vector, you might as well write it as v.xxxx * v2. A third reason swizzles are useful on GPUs is because you often end up stuffing completely unrelated junk into them in the name of efficiency. I'm not sure that's necessary or useful on a CPU architecture that isn't quite as tied to float4 as GPUs are. -- I'm sure I'm among those who would use built-in small vector classes, but I don't think it's clear that they should be built into the compiler of a general purpose programming language. On the other hand, if you can convince me that it really is impossible to maximize performance (while maintaining convenience) any other way, then I could be swayed. Also if CPUs themselves are moving in this direction, then that also is something to think about. By that I mean if float4 becomes (or already is) what could be considered a "native type" on the major desktop CPUs, then I can see that it would make sense for a programming language to reflect that by making it a built-in type. --bb
Jan 28 2007
Bill Baxter wrote:Mikola Lysenko wrote:What applications don't use vector instructions? Also, I think it's more important to consider what /D applications/ will be using SIMD instructions, rather than what applications in general do or do not use coordinate geometry. That's because a lot of those applications may not even be written in D or have anything to do with D, like the mass of stuff written in dynamic languages like perl, python, ruby, etc. I have to wonder, has any language out there really given good support for SIMD primitives, besides assembly? I think D could stand a lot to gain here. That said, I don't mind if it's done in a library as long as it looks polished and is not cumbersome.I'll bite. Here are the two features I consider most important: 2. Low dimensional vectors as primitive types Specifically I would like to see the types int, real, float etc. extended into 2-4 dimensional vectors. ie. int2, real4, float3. This would be exceptionally useful in many applications which require coordinate geometry. Here is a very brief list: Scientific Programs Physics Simulation Computer Graphics Video Games User Interfaces Computational Geometry Robotics Fluid SimulationIt's still a tiny fraction of the number of applications that use, say, strings. So the ubiquity argument for inclusion is pretty weak, I think.I'd say float4 has been a native type for a couple years now. A desktop computer that doesn't have SSE or Altivec or some other SIMD is probably quite antiquated and not running D programs. This is because SSE was around in 1999 running on 450 MHz CPUs. http://en.wikipedia.org/wiki/Streaming_SIMD_Extensions The only computers I know of that lack float4 are smartphone and PDA type devices running modern ARM processors. Even some of the recent ARM-XSCALE processors have MMX instructions, which doesn't give float4 but does give short4 and int2. I'm also not sure about modern supercomputers and the like, since I haven't worked with those.etc. I would prefer not to recount the number of times I have written my own vector library, and how tedious they are to create. For most every language I learn it is the first thing I need to write, since so few are willing to provide a default implementation. In my opinion, this is unacceptable for a problem which occurs so frequently.Again, it occurs for *you* frequently (and I'll admit for me too), but still the vast majority of programmers out there have never had a burning need for a float3 with all the bells and whistles. If the need for a vector library were truly ubiquitous, it seems like it would be easier to find a decent implementation on the web, or that one would at least be available in the standard library of the given programming language. As far as D is concerned, Helix has a pretty decent implementation. See http://www.dsource.org/projects/helix. It lacks Vector2's but I've added them to my own copy and I'd be happy to send it to you if you like.One option is to extend the standard library with a vector-types class, but this is not nearly as nice a compiler level implementation.I'm not convinced that a compiler-level implementation of these things is necessary.1. The 90 degrees rotation trick This is based on the following article: http://www.flipcode.com/articles/article_fastervectormath.shtml ... The performance improvement becomes more substantial the longer the expression. Since overloaded operators do not instantiate templates, there is no obvious way to obtain this result in the current language spec.I thought the new opAssign was supposed to be enough to make expression templates work in D. Don Clugston even posted a proof-of-concept that would use templates to rearrange expressions a while back. Anyway, for this one, I think preferred approach is to make the core language expressive enough so that tricks like expression templates can work, rather than implementing such optimizations for particular cases in the compiler.2. Architecture specific optimizations (SIMD) For low dimensional arithmetic, many architectures provide specially optimized instruction for low dimensional vectors. The problem is most languages do not exploit them. Creating efficient SIMD code is impossible for a library, since each opAdd/opMul must be written using inline assembler and therefore incurs the overhead of a function call regardless. This is worsened by the fact that moving to/from a vector register is typically very expensive. A compiler level implementation can easily avoid these issues by assigning vector expressions to a register when passing them. Moreover it is more portable than compiler intrinsics like MSVC's SSE extensions. The implementation can easily emit fallback code if the architecture does not support SIMD instructions.Again, this sounds like it would be better to solve the generic issue of libraries not being able to take maximum advantage of existing hardware optimizations, like the issue with ASM methods not being inline-able.3. Swizzles A swizzle is a reordering of the elements in a vector. Shader languages like Cg or GLSL typically support them, given their utility in certain types of computations. Here are some examples: v.xxxx // Returns a vector with v.x broadcast to all components v.xyz // Returns only the xyz components of v v.zyx // Returns a vector consisting of the reverse of v's xyz components Enumerating all possible swizzles within a template is impossible, and therefore requires one function per swizzle. The result is massive code bloat, and many lines of automatically generated gibberish. To get an idea at how many functions this requires, the total number of swizzles for 2-4 component vectors is 4^4 + 4^3 + 4^2 + 4 or 340. Multiply that by the number of primitive types and the result becomes quite large.Are swizzles all that useful outside of Shader languages? Part of the reason they are useful in shaders is that GPU's can do a swizzles for free. Can CPUs (I dunno)? Another part of the reason is that all operations happen on 4-components no matter what, so if you want to multiply a scalar inside a vector times another vector, you might as well write it as v.xxxx * v2. A third reason swizzles are useful on GPUs is because you often end up stuffing completely unrelated junk into them in the name of efficiency. I'm not sure that's necessary or useful on a CPU architecture that isn't quite as tied to float4 as GPUs are. -- I'm sure I'm among those who would use built-in small vector classes, but I don't think it's clear that they should be built into the compiler of a general purpose programming language. On the other hand, if you can convince me that it really is impossible to maximize performance (while maintaining convenience) any other way, then I could be swayed. Also if CPUs themselves are moving in this direction, then that also is something to think about. By that I mean if float4 becomes (or already is) what could be considered a "native type" on the major desktop CPUs, then I can see that it would make sense for a programming language to reflect that by making it a built-in type. --bb
Jan 28 2007
Chad J wrote:The only computers I know of that lack float4 are smartphone and PDA type devices running modern ARM processors. Even some of the recent ARM-XSCALE processors have MMX instructions, which doesn't give float4 but does give short4 and int2. I'm also not sure about modern supercomputers and the like, since I haven't worked with those.Actually, newer ARM devices support the NEON/VFP instruction set extensions, which provides SIMD capabilities similar to SSE. It is strange that most modern ISA's have better support for geometric vectors than higher level programming languages... -Mik
Jan 28 2007
Chad J wrote:Bill Baxter wrote:Business applications? I'm not sure what you mean exactly. But the question is not so much does app xyz use vector instructions, but rather would the programmer of app xyz have benefited from having int2/3/4 or float2/3/4 type primitives.Mikola Lysenko wrote:What applications don't use vector instructions?I'll bite. Here are the two features I consider most important: 2. Low dimensional vectors as primitive types Specifically I would like to see the types int, real, float etc. extended into 2-4 dimensional vectors. ie. int2, real4, float3. This would be exceptionally useful in many applications which require coordinate geometry. Here is a very brief list: Scientific Programs Physics Simulation Computer Graphics Video Games User Interfaces Computational Geometry Robotics Fluid SimulationIt's still a tiny fraction of the number of applications that use, say, strings. So the ubiquity argument for inclusion is pretty weak, I think.Also, I think it's more important to consider what /D applications/ will be using SIMD instructions, rather than what applications in general do or do not use coordinate geometry. That's because a lot of those applications may not even be written in D or have anything to do with D, like the mass of stuff written in dynamic languages like perl, python, ruby, etc.Well judging from posts around here it seems like web programming folks and DB users make up a significant percentage of active D users. But gamers and scientific folk are another significant group it seems.I have to wonder, has any language out there really given good support for SIMD primitives, besides assembly? I think D could stand a lot to gain here. That said, I don't mind if it's done in a library as long as it looks polished and is not cumbersome.Agreed. --bb
Jan 28 2007
Bill Baxter wrote:Mikola Lysenko wrote:This was the same sentiment I felt several years ago, but overtime I have come to realize that vectors are probably one of the most widely used, yet least supported programming structures. The thought eventually crystallized for me while learning ActionScript 3. I was reading through the example projects they provided with the SDK, and was struck by the fact that each project used a custom 2-d vector class. Given the prevalence of such a construct, it seems that a language ought to provide a good common implementation. Pretty much any application that eventually needs to produce graphical output requires the use of vectors somewhere down the line. Be it a simple GUI reading in mouse clicks, a video game or a scientific GUI. The idea of low dimensional vectors is firmly rooted in geometry, which is one of the most basic branches of mathematics. Vectors are easily used more often than complex numbers, which D already supports. I would like to see some more detailed usage statistics on vectors, however this is difficult to reliably check. A Google code search on vector vs. complex gives the following results, though this may be somewhat skewed given that vector is a loaded word in computer science: Vector Math: 300k results http://www.google.com/codesearch?q=vector+math&hl=en&btnG=Search+Code Complex Math: 100k results http://www.google.com/codesearch?q=complex+math&hl=en&btnG=Search+Code I suspect that there are probably better ways to measure the use of vectors, especially since the synonym Point is often used in C++ programs. (std::vector creates name conflicts). Also contractions like vec, vect, vecr are often used instead of a full word. As a percentage of total applications containing vectors, it may be difficult to estimate. From my personal experience, I would guess that they are used very frequently. For such a widespread structure, there is remarkably little language support. For example, the Java standard library contains hundreds of classes. It has everything from a GregorianCalendar to a Midi synthesizer. It does not have vector support. Even in newer languages like Python, there is still an inordinate amount of time wasted on reimplementing vectors. Part of the problem is that vectors are pretty easy to hack together, so everyone basically shlocks something that barely works and uses that. The other option is that they get hung up on the damn vector class before they even write the project and end up wasting all their time on that instead. There are literally hundreds of vector libraries for C++ alone. A Google search for vector library gives over 26 million results! I've even contributed a few of my own, such as: http://www.assertfalse.com/downloads/vecmat-0.1.zip Moreover, interoperability is a big concern. Many times I have tried to interface with some code online, perhaps a model loader or some numerical application. Since so few languages have standard vectors, they all use custom formats for processing data. In order to add them to my project, I typically have to do a find/replace on the vector type they are using and pray to god it compiles. Even then, there are sometimes sneaking problems that only show up after I've finished importing and connecting everything.2. Low dimensional vectors as primitive types This would be exceptionally useful in many applications which require coordinate geometry. Here is a very brief list: Scientific Programs Physics Simulation Computer Graphics Video Games User Interfaces Computational Geometry Robotics Fluid SimulationIt's still a tiny fraction of the number of applications that use, say, strings. So the ubiquity argument for inclusion is pretty weak, I think.As far as D is concerned, Helix has a pretty decent implementation. See http://www.dsource.org/projects/helix. It lacks Vector2's but I've added them to my own copy and I'd be happy to send it to you if you like.Helix is not a bad project, but in order for a standard vector to be useful it needs to come packaged with the compiler. The temptation to roll your own is too great otherwise. Even putting it in the standard library is not enough to prevent people from reinventing the wheel, as we have seen by the numerous variations on C++'s std::list. Ideally, small vectors should be in the global namespace, right alongside complex numbers and dynamic arrays. Effective vector code needs correct data alignment, instruction scheduling and register use. Each of these issues is most effectively handled in the compiler/code gen stage, and therefore suggests that at the very least the compiler implementation ought to be aware of the vector type in some way. By applying the "D Builtin Rationale," it is easy to see that vectors meet all the required criteria. An optimal strategy would be to have the vector types builtin, with a simple per-component multiplication defined, and a separate standard math library for doing more complex operations. Here is an example: import std.vecmath; float4 a = [1, 2, 3, 4]; float4 b = [2, 3, 4, 5]; float4 c = a * b; //c = [2, 6, 12, 20] float d = dot(a, b); //Defined in std.vecmath -MikOne option is to extend the standard library with a vector-types class, but this is not nearly as nice a compiler level implementation.I'm not convinced that a compiler-level implementation of these things is necessary.
Jan 28 2007
Mikola Lysenko wrote:Helix is not a bad project, but in order for a standard vector to be useful it needs to come packaged with the compiler. The temptation to roll your own is too great otherwise. Even putting it in the standard library is not enough to prevent people from reinventing the wheel, as we have seen by the numerous variations on C++'s std::list. Ideally, small vectors should be in the global namespace, right alongside complex numbers and dynamic arrays.Hmmm… D’s complex types seem to be syntactic sugar over a construct much like C++’s complex template. I’d think that a template library ought to come first, with incorporation into the language later.Effective vector code needs correct data alignment, instruction scheduling and register use. Each of these issues is most effectively handled in the compiler/code gen stage, and therefore suggests that at the very least the compiler implementation ought to be aware of the vector type in some way. By applying the "D Builtin Rationale," it is easy to see that vectors meet all the required criteria.As I understand it, D’s inline assembler would be the tool to use for this in a library implementation. I don’t think the complex types use SIMD, so the vectors can be the only things using those registers.An optimal strategy would be to have the vector types builtin, with a simple per-component multiplication defined, and a separate standard math library for doing more complex operations. Here is an example: import std.vecmath; float4 a = [1, 2, 3, 4]; float4 b = [2, 3, 4, 5]; float4 c = a * b; //c = [2, 6, 12, 20] float d = dot(a, b); //Defined in std.vecmathConsidering that the primary vector types for physics or 3-D animation are 3-tuples (v ∊ ℝ³) and Hamiltonian quaternions (q ∊ ℍ or q ∊ ℝ⁴), and by extension with the complex types, how about a v prefix for 3-tuples and an h prefix for quaternions: vfloat v = [1, 2, 3]; // vfloat ≈ float3 hreal q = [2, 3, 4, 5]; // hreal ≈ real4 and the additional operators needed to handle them “right”: vfloat a = [1, 2, 3]; vfloat b = [2, 3, 4]; vfloat p = a*b; // element-wise product, [1*2, 2*3, 3*4] float d = a•b; // dot product, [1*2 + 2*3 + 3*4] vfloat c = a×b // cross product Some notation for inverses (a postfix operator ⁻¹, perhaps) and notation for 3×3 and 4×4 matrices would be useful too, along with defining the dot and cross product for the complex numbers… … or this can all go in a library until the interface and implementation are well defined and understood. I’m only just learning the language and I’m watching it grow. --Joel
Jan 28 2007
Joel C. Salomon wrote:As I understand it, D’s inline assembler would be the tool to use for this in a library implementation. I don’t think the complex types use SIMD, so the vectors can be the only things using those registers.I can tell you right now that this won't work. I have tried using the inline assembler with a vector class and the speedup was at barely noticeable. You can see the results here: http://assertfalse.com Here are just a few of the things that become a problem for a library implementation: 1. Function calls Inline assmeber can not be inlined. Period. The compiler has to think of inline assembler as a sort of black box, which takes inputs one way and returns them another way. It can not poke around in there and change your hand-tuned opcodes in order to pass arguments in arguments more efficiently. Nor can it change the way you allocate registers so you don't accidentally trash the local frame. It can't manipulate where you put the result, such that it can be used immediately by the next block of code. Therefore any asm vector class will have a lot of wasteful function calls which quickly add up: a = b + c * d; becomes: a = b.opAdd(c.opMul(d)); 2. Register allocation This point is related to 1. Most SIMD architectures have many registers, and a good compiler can easily use that to optimize stuff like parameter passing and function returns. This is totally impossible for a library to do, since it has no knowledge of the contents of any registers as it executes. 3. Data alignment This is a big problem for libraries. Most vector architectures require properly aligned data. D only provides facilities for aligning attributes within a struct, not according to any type of global system alignment. To fix this in D, we will need the compiler's help. This will allow us to pack vectors in a function such that they are properly aligned within each local call frame. -Mik
Jan 29 2007
Mikola Lysenko wrote:Joel C. Salomon wrote:Can GCC-like extended assembler (recently implemented in GDC: http://dgcc.sourceforge.net/gdc/manual.html) help for these first two points? It allows you to let the compiler allocate registers. That should fix point two. You can also tell the compiler where to put variables and where you're going to put any results. That means your asm doesn't necessarily need to access memory to do anything useful. If the compiler sees it doesn't inlining the function should be possible, I think. It won't fix all asm, of course, but it might make it possible to write inlinable asm. I do think it needs different syntax. Strings? AT&T asm syntax? Bah. But the idea itself is a good one, I think.As I understand it, D’s inline assembler would be the tool to use for this in a library implementation. I don’t think the complex types use SIMD, so the vectors can be the only things using those registers.I can tell you right now that this won't work. I have tried using the inline assembler with a vector class and the speedup was at barely noticeable. You can see the results here: http://assertfalse.com Here are just a few of the things that become a problem for a library implementation: 1. Function calls Inline assmeber can not be inlined. Period. The compiler has to think of inline assembler as a sort of black box, which takes inputs one way and returns them another way. It can not poke around in there and change your hand-tuned opcodes in order to pass arguments in arguments more efficiently. Nor can it change the way you allocate registers so you don't accidentally trash the local frame. It can't manipulate where you put the result, such that it can be used immediately by the next block of code. Therefore any asm vector class will have a lot of wasteful function calls which quickly add up: a = b + c * d; becomes: a = b.opAdd(c.opMul(d)); 2. Register allocation This point is related to 1. Most SIMD architectures have many registers, and a good compiler can easily use that to optimize stuff like parameter passing and function returns. This is totally impossible for a library to do, since it has no knowledge of the contents of any registers as it executes.
Jan 29 2007
Frits van Bommel wrote:Can GCC-like extended assembler (recently implemented in GDC: http://dgcc.sourceforge.net/gdc/manual.html) help for these first two points? It allows you to let the compiler allocate registers. That should fix point two. You can also tell the compiler where to put variables and where you're going to put any results. That means your asm doesn't necessarily need to access memory to do anything useful. If the compiler sees it doesn't inlining the function should be possible, I think. It won't fix all asm, of course, but it might make it possible to write inlinable asm.For regular functions, I completely agree. The system should work at least as well as compiler inlining (for certain cases). Unfortunately vector assembler is not the same. At the very minimum the implementation needs to be aware of the vector registers on the system, and it needs to be able to pass parameters/returns in those registers. Otherwise, you still have to use costly functions like movups, and you still pay the same basic bookkeeping costs. It just seems like it would be simpler to make small vectors primitive types and be done with it. -Mik
Jan 29 2007
Mikola Lysenko wrote:It just seems like it would be simpler to make small vectors primitive types and be done with it.If you could write up a short proposal on exactly what operations should be supported, alignment, etc., that would be most helpful.
Jan 29 2007
Mikola Lysenko wrote:Inline assmeber can not be inlined. Period. The compiler has to think of inline assembler as a sort of black box, which takes inputs one way and returns them another way. It can not poke around in there and change your hand-tuned opcodes in order to pass arguments in arguments more efficiently. Nor can it change the way you allocate registers so you don't accidentally trash the local frame. It can't manipulate where you put the result, such that it can be used immediately by the next block of code. Therefore any asm vector class will have a lot of wasteful function calls which quickly add up:Sounds like what’s needed is in-line intermediate code rather than in-line assembly — or, at least some way to tell the compiler “do this operation on some block of eight SIMD registers; I don’t particularly care which.” One way do do this could be with something like C’s ‘register’ keyword. Not sure on D’s inline assembler syntax, but a possible solution could look like: register int a; asm{ addl 0x32, $a } where the compiler substitutes the allocated register (say ECX) for $a. With a little extra syntax to say what sort of register is required, we get all the power your vector library needs without adding yet another tacked-on feature. Plus, while you get your vectors, I can get my quaternions and matrix manipulation for the same price. --Joel
Jan 30 2007
Joel C. Salomon wrote:Mikola Lysenko wrote:GCC (and recent GDC) already has such a mechanism, they call it 'extended assembler'. The basic idea is that you can specify several substitutions for registers. You can have them pre-filled by GCC with specific expressions, tell GCC to put certain registers in variables afterwards, and specify what other registers (and optionally "memory") may be changed afterwards. (That last part could perhaps be omitted if the compiler did some extra work to analyze the asm?) The GCC version has pretty ugly syntax (IMHO), and I have no idea if they support SIMD registers with it, but I've always liked the idea itself. But I've already mentioned much of this in a previous post in this thread.Inline assmeber can not be inlined. Period. The compiler has to think of inline assembler as a sort of black box, which takes inputs one way and returns them another way. It can not poke around in there and change your hand-tuned opcodes in order to pass arguments in arguments more efficiently. Nor can it change the way you allocate registers so you don't accidentally trash the local frame. It can't manipulate where you put the result, such that it can be used immediately by the next block of code. Therefore any asm vector class will have a lot of wasteful function calls which quickly add up:Sounds like what’s needed is in-line intermediate code rather than in-line assembly — or, at least some way to tell the compiler “do this operation on some block of eight SIMD registers; I don’t particularly care which.”
Jan 30 2007
Joel C. Salomon wrote:SIMD instructions can also be useful for complex operations, so I don't think that's a safe assumption to make. http://www.intel.com/cd/ids/developer/asmo-na/eng/dc/pentium4/optimization/66717.htm --bbEffective vector code needs correct data alignment, instruction scheduling and register use. Each of these issues is most effectively handled in the compiler/code gen stage, and therefore suggests that at the very least the compiler implementation ought to be aware of the vector type in some way. By applying the "D Builtin Rationale," it is easy to see that vectors meet all the required criteria.As I understand it, D’s inline assembler would be the tool to use for this in a library implementation. I don’t think the complex types use SIMD, so the vectors can be the only things using those registers.
Jan 29 2007
Mikola Lysenko wrote:Bill Baxter wrote:"Vectors are more common than complex, and complex is built-in". This is a good argument. Although, there seem to be a number of things out there that are more common, or least as-common as 'complex'. For instance, there's a contingent here that wants to see decimal arithmetic supported in the core. There was a strong push for regexp to be in the core for a while. "Most CPUs today have *some* kind of SSE/Altivec type thing" That may be, but I've heard that at least SSE is really not that suited to many calculations -- especially ones in graphics. Something like you have to pack your data so that all the x components are together, and all y components together, and all z components together. Rather than the way everyone normally stores these things as xyz, xyz. Maybe Altivec, SSE2 and SSE3 fix that though. At any rate I think maybe Intel's finally getting tired of being laughed at for their graphics performance so things are probably changing. "Library vs Core" I think there's really not much that you can ask from the core. A small vector of 4 numbers can represent any number of things. So I think your best hope for the core is to support some very basic operations on small vectors -- like component-wise +,-,*,/, and dot product -- to optimize those kind of expressions as best as possible, and leave everything else to libraries. I guess that's pretty much how it works with HW shader languages. Except they add swizzles to the set of primitive ops. So what you're left with would be satisfied in large part by just having vector math work on small static arrays (and be optimized well). float[4] v = a*b + c*d; you can even make aliases if you like. alias float[4] float4; But your 3D graphics library will still need to be a library on top of that. Good news is that I think vector math like the above is something that Walter is interested in adding to the language at some point. Bad news is that noone knows when that will be. I'd guess though that a kick-ass vector expression optimizer would be a long way off, judging by how hard it's been for C++ compilers to get SSE type optimizations implemented. "Getting it in the standard library" I agree, though, that lo-D math is common enough that it should be included in a standard library. I wonder if the Tango developers would be willing to include a vector math class...or if they already have one in there. I'd certainly like to be a contributor to that if so. --bbMikola Lysenko wrote:2. Low dimensional vectors as primitive types
Jan 28 2007
Bill Baxter wrote:Mikola Lysenko wrote:Yeah I mentioned static arrays earlier, but I realized that they can be a bit of a pain in the ass to work with. This probably means that static arrays should be fixed, then they could be useful vectors. Consider trying to return a float4 from a function. You can't return a float[4]. I remember talk on this ng about how static arrays were this kinda sucky inbetween-value-and-reference-type sort of thing. I'd hope that at least gets fixed before static arrays are used for lo-D vectors.Bill Baxter wrote:"Vectors are more common than complex, and complex is built-in". This is a good argument. Although, there seem to be a number of things out there that are more common, or least as-common as 'complex'. For instance, there's a contingent here that wants to see decimal arithmetic supported in the core. There was a strong push for regexp to be in the core for a while. "Most CPUs today have *some* kind of SSE/Altivec type thing" That may be, but I've heard that at least SSE is really not that suited to many calculations -- especially ones in graphics. Something like you have to pack your data so that all the x components are together, and all y components together, and all z components together. Rather than the way everyone normally stores these things as xyz, xyz. Maybe Altivec, SSE2 and SSE3 fix that though. At any rate I think maybe Intel's finally getting tired of being laughed at for their graphics performance so things are probably changing. "Library vs Core" I think there's really not much that you can ask from the core. A small vector of 4 numbers can represent any number of things. So I think your best hope for the core is to support some very basic operations on small vectors -- like component-wise +,-,*,/, and dot product -- to optimize those kind of expressions as best as possible, and leave everything else to libraries. I guess that's pretty much how it works with HW shader languages. Except they add swizzles to the set of primitive ops. So what you're left with would be satisfied in large part by just having vector math work on small static arrays (and be optimized well). float[4] v = a*b + c*d; you can even make aliases if you like. alias float[4] float4; But your 3D graphics library will still need to be a library on top of that.Mikola Lysenko wrote:2. Low dimensional vectors as primitive typesGood news is that I think vector math like the above is something that Walter is interested in adding to the language at some point. Bad news is that noone knows when that will be. I'd guess though that a kick-ass vector expression optimizer would be a long way off, judging by how hard it's been for C++ compilers to get SSE type optimizations implemented. "Getting it in the standard library" I agree, though, that lo-D math is common enough that it should be included in a standard library. I wonder if the Tango developers would be willing to include a vector math class...or if they already have one in there. I'd certainly like to be a contributor to that if so. --bb
Jan 28 2007
Chad J wrote:Bill Baxter wrote:Mikola Lysenko wrote:[...]Bill Baxter wrote:Mikola Lysenko wrote:2. Low dimensional vectors as primitive typesAgreed. So basically the D 2.0 wish list item becomes to make float[4] act more like a plain-old-data value type, give it simple math operations, optimize the heck out of it, and make it possible to alias it to float4 for convenience. And maybe add the .xyzw accessors and swizzles too. :-) I suspect the "optimize the heck out of it" step will be a long time coming though. Probably more like D 3.0. --bbfloat[4] v = a*b + c*d; you can even make aliases if you like. alias float[4] float4; But your 3D graphics library will still need to be a library on top of that.Yeah I mentioned static arrays earlier, but I realized that they can be a bit of a pain in the ass to work with. This probably means that static arrays should be fixed, then they could be useful vectors.
Jan 28 2007
Bill Baxter wrote:Chad J wrote:*sigh* but I want my uber vector operations NOW! :)Bill Baxter wrote:Mikola Lysenko wrote:[...]Bill Baxter wrote:Mikola Lysenko wrote:2. Low dimensional vectors as primitive typesAgreed. So basically the D 2.0 wish list item becomes to make float[4] act more like a plain-old-data value type, give it simple math operations, optimize the heck out of it, and make it possible to alias it to float4 for convenience. And maybe add the .xyzw accessors and swizzles too. :-) I suspect the "optimize the heck out of it" step will be a long time coming though. Probably more like D 3.0. --bbfloat[4] v = a*b + c*d; you can even make aliases if you like. alias float[4] float4; But your 3D graphics library will still need to be a library on top of that.Yeah I mentioned static arrays earlier, but I realized that they can be a bit of a pain in the ass to work with. This probably means that static arrays should be fixed, then they could be useful vectors.
Jan 28 2007
Bill Baxter wrote:"Most CPUs today have *some* kind of SSE/Altivec type thing" That may be, but I've heard that at least SSE is really not that suited to many calculations -- especially ones in graphics. Something like you have to pack your data so that all the x components are together, and all y components together, and all z components together. Rather than the way everyone normally stores these things as xyz, xyz. Maybe Altivec, SSE2 and SSE3 fix that though. At any rate I think maybe Intel's finally getting tired of being laughed at for their graphics performance so things are probably changing.I have never heard of any SIMD architecture where vectors works that way. On SSE, Altivec or MMX the components for the vectors are always stored in contiguous memory. In terms of graphics, this is pretty much optimal. Most manipulations on vectors like rotations, normalization, cross product etc. require access to all components simultaneously. I honestly don't know why you would want to split each of them into separate buffers... Surely it is simpler to do something like this: x y z w x y z w x y z w ... vs. x x x x ... y y y y ... z z z z ... w w w ..."Library vs Core" I think there's really not much that you can ask from the core. A small vector of 4 numbers can represent any number of things. So I think your best hope for the core is to support some very basic operations on small vectors -- like component-wise +,-,*,/, and dot product -- to optimize those kind of expressions as best as possible, and leave everything else to libraries. I guess that's pretty much how it works with HW shader languages. Except they add swizzles to the set of primitive ops.Yes, I think this is probably the best course of action. Because of the nature of vectors, and the fact that they require such careful compiler integration, they must be placed in the core language. On the other hand, most products like dot, cross, perp or outer should be in a library. The reason for this is that there are simply too many types of products and operators for a language to reasonably support them all. At any rate, once you have the basics you can quickly build up the others. Here is an example of a cross product: float3 cross(float3 a, float3 b) { return a.yzx * b.zxy - a.zxy * b.yzx; } Implementing most products and vector operations is easy once you have a simple component-wise vector library."Getting it in the standard library" I agree, though, that lo-D math is common enough that it should be included in a standard library. I wonder if the Tango developers would be willing to include a vector math class...or if they already have one in there.It may eventually get in. However, it would be far more optimal if the language simply supported them in the core spec. -Mik
Jan 29 2007
Mikola Lysenko wrote:Bill Baxter wrote:Ok. Well, I've never used any of these MMX/SSE/Altivec things myself, so it was just heresay. But the source was someone I know in the graphics group at Intel. I must have just misunderstood his gripe, in that case."Most CPUs today have *some* kind of SSE/Altivec type thing" That may be, but I've heard that at least SSE is really not that suited to many calculations -- especially ones in graphics. Something like you have to pack your data so that all the x components are together, and all y components together, and all z components together. Rather than the way everyone normally stores these things as xyz, xyz. Maybe Altivec, SSE2 and SSE3 fix that though. At any rate I think maybe Intel's finally getting tired of being laughed at for their graphics performance so things are probably changing.I have never heard of any SIMD architecture where vectors works that way. On SSE, Altivec or MMX the components for the vectors are always stored in contiguous memory.In terms of graphics, this is pretty much optimal. Most manipulations on vectors like rotations, normalization, cross product etc. require access to all components simultaneously. I honestly don't know why you would want to split each of them into separate buffers... Surely it is simpler to do something like this: x y z w x y z w x y z w ... vs. x x x x ... y y y y ... z z z z ... w w w ...Yep, I agree, but I thought that was exactly the gist of what this friend of mine was griping about. As I understood it at the time, he was complaining that the CPU instructions are good at planar layout x x x x y y y y ... but not interleaved x y x y x y. If that's not the case, then great. --bb
Jan 29 2007
Bill Baxter wrote:Mikola Lysenko wrote:Seems it's great. It doesn't really matter what the underlying data is. An SSE instruction will add four 32-bit floats in parallel, nevermind whether the floats are x x x x or x y z w. What meaning the floats have is up to the programmer. Of course, channelwise operations will be faster in planer (EX: add 24 to all red values, don't spend time on the other channels), while pixelwise operations will be faster in interleaved (EX: alpha blending) - these facts don't have much to do with SIMD. Maybe the guy from intel wanted to help planar pixelwise operations (some mechanism to help the need to dereference 3-4 different places at once) or help interleaved channelwise operations (only operate on every fourth float in an array without having to do 4 mov/adds to fill a 128 bit register).Bill Baxter wrote:Ok. Well, I've never used any of these MMX/SSE/Altivec things myself, so it was just heresay. But the source was someone I know in the graphics group at Intel. I must have just misunderstood his gripe, in that case."Most CPUs today have *some* kind of SSE/Altivec type thing" That may be, but I've heard that at least SSE is really not that suited to many calculations -- especially ones in graphics. Something like you have to pack your data so that all the x components are together, and all y components together, and all z components together. Rather than the way everyone normally stores these things as xyz, xyz. Maybe Altivec, SSE2 and SSE3 fix that though. At any rate I think maybe Intel's finally getting tired of being laughed at for their graphics performance so things are probably changing.I have never heard of any SIMD architecture where vectors works that way. On SSE, Altivec or MMX the components for the vectors are always stored in contiguous memory.In terms of graphics, this is pretty much optimal. Most manipulations on vectors like rotations, normalization, cross product etc. require access to all components simultaneously. I honestly don't know why you would want to split each of them into separate buffers... Surely it is simpler to do something like this: x y z w x y z w x y z w ... vs. x x x x ... y y y y ... z z z z ... w w w ...Yep, I agree, but I thought that was exactly the gist of what this friend of mine was griping about. As I understood it at the time, he was complaining that the CPU instructions are good at planar layout x x x x y y y y ... but not interleaved x y x y x y. If that's not the case, then great. --bb
Jan 29 2007
Chad J wrote:Bill Baxter wrote:That could be. I seem to remember now the specific thing we were talking about was transforming a batch of vectors. Is there a good way to do that with SSE stuff? I.e for a 4x4 matrix with rows M1,M2,M3,M4 you want to do something like: foreach(i,v; vector_batch) out[i] = [dot(M1,v),dot(M2,v),dot(M3,v),dot(M4,v)]; Maybe it had to do with not being able to operate 'horizontally'. E.g. to do a dot product you can multiply x y z w times a b c d easily, but then you need the sum of those. Apparently SSE3 has some instructions to help this case some. You can add x+y and z+w in one step. By the way, are there any good tutorials on programming with SIMD (specifically for Intel/AMD)? Everytime I've looked I come up with pretty much nothing. Googling for "SSE tutorial" doesn't result in much. As far as making use of SIMD goes (in C++), I ran across this project that looks very promising, but have yet to give it a real try: http://www.pixelglow.com/macstl/ --bbMikola Lysenko wrote:Seems it's great. It doesn't really matter what the underlying data is. An SSE instruction will add four 32-bit floats in parallel, nevermind whether the floats are x x x x or x y z w. What meaning the floats have is up to the programmer. Of course, channelwise operations will be faster in planer (EX: add 24 to all red values, don't spend time on the other channels), while pixelwise operations will be faster in interleaved (EX: alpha blending) - these facts don't have much to do with SIMD. Maybe the guy from intel wanted to help planar pixelwise operations (some mechanism to help the need to dereference 3-4 different places at once) or help interleaved channelwise operations (only operate on every fourth float in an array without having to do 4 mov/adds to fill a 128 bit register).Bill Baxter wrote:Yep, I agree, but I thought that was exactly the gist of what this friend of mine was griping about. As I understood it at the time, he was complaining that the CPU instructions are good at planar layout x x x x y y y y ... but not interleaved x y x y x y. If that's not the case, then great. --bb
Jan 29 2007
Chad J wrote:Bill Baxter wrote:Sorry to keep harping on this, but here's an article that basically says exactly what my friend was saying. http://www.anandtech.com/cpuchipsets/showdoc.aspx?i=2350 From the article: """ The hadd and hsub instructions are horizontal additions and horizontal subtractions. These allow faster processing of data stored "horizontally" in (for example) vertex arrays. Here is a 4-element array of vertex structures. x1 y1 z1 w1 | x2 y2 z2 w2 | x3 y3 z3 w3 | x4 y4 z4 w4 SSE and SSE2 are organized such that performance is better when processing vertical data, or structures that contain arrays; for example, a vertex structure with 4-element arrays for each component: x1 x2 x3 x4 y1 y2 y3 y4 z1 z2 z3 z4 w1 w2 w3 w4 Generally, the preferred organizational method for vertecies is the former. Under SSE2, the compiler (or very unfortunate programmer) would have to reorganize the data during processing. """ The article is talking about how hadd and hsub in SSE3 help to corrects the situation, but SSE3 isn't yet nearly as ubiquitous as SSE/SSE2, I would imagine. --bbMikola Lysenko wrote:Seems it's great. It doesn't really matter what the underlying data is. An SSE instruction will add four 32-bit floats in parallel, nevermind whether the floats are x x x x or x y z w. What meaning the floats have is up to the programmer. Of course, channelwise operations will be faster in planer (EX: add 24 to all red values, don't spend time on the other channels), while pixelwise operations will be faster in interleaved (EX: alpha blending) - these facts don't have much to do with SIMD. Maybe the guy from intel wanted to help planar pixelwise operations (some mechanism to help the need to dereference 3-4 different places at once) or help interleaved channelwise operations (only operate on every fourth float in an array without having to do 4 mov/adds to fill a 128 bit register).Bill Baxter wrote:Ok. Well, I've never used any of these MMX/SSE/Altivec things myself, so it was just heresay. But the source was someone I know in the graphics group at Intel. I must have just misunderstood his gripe, in that case."Most CPUs today have *some* kind of SSE/Altivec type thing" That may be, but I've heard that at least SSE is really not that suited to many calculations -- especially ones in graphics. Something like you have to pack your data so that all the x components are together, and all y components together, and all z components together. Rather than the way everyone normally stores these things as xyz, xyz. Maybe Altivec, SSE2 and SSE3 fix that though. At any rate I think maybe Intel's finally getting tired of being laughed at for their graphics performance so things are probably changing.I have never heard of any SIMD architecture where vectors works that way. On SSE, Altivec or MMX the components for the vectors are always stored in contiguous memory.In terms of graphics, this is pretty much optimal. Most manipulations on vectors like rotations, normalization, cross product etc. require access to all components simultaneously. I honestly don't know why you would want to split each of them into separate buffers... Surely it is simpler to do something like this: x y z w x y z w x y z w ... vs. x x x x ... y y y y ... z z z z ... w w w ...Yep, I agree, but I thought that was exactly the gist of what this friend of mine was griping about. As I understood it at the time, he was complaining that the CPU instructions are good at planar layout x x x x y y y y ... but not interleaved x y x y x y. If that's not the case, then great. --bb
Jan 29 2007
Bill Baxter wrote:Chad J wrote:That makes a lot of sense. I also remember running into trouble finding material on SSE as well. I never really got past looking at what all of the instructions do, or maybe implementing an algorithm or two. I would have needed the SSE2 instructions to do the integer stuff that I wanted to do, and I don't think I even had those on my old computer when I was doing this stuff :/ For my purposes, MMX was much easier to use and find resources for. You'll probably have better luck searching for "SSE Instruction Set" and just messing around with the instructions (probably what I'd do). There should also be some (probably meager) Intel documentation and comments on SSE. Here are some pages I found: http://softpixel.com/~cwright/programming/simd/sse.php http://www.cpuid.com/sse.php http://www.hayestechnologies.com/en/techsimd.htmBill Baxter wrote:Sorry to keep harping on this, but here's an article that basically says exactly what my friend was saying. http://www.anandtech.com/cpuchipsets/showdoc.aspx?i=2350 From the article: """ The hadd and hsub instructions are horizontal additions and horizontal subtractions. These allow faster processing of data stored "horizontally" in (for example) vertex arrays. Here is a 4-element array of vertex structures. x1 y1 z1 w1 | x2 y2 z2 w2 | x3 y3 z3 w3 | x4 y4 z4 w4 SSE and SSE2 are organized such that performance is better when processing vertical data, or structures that contain arrays; for example, a vertex structure with 4-element arrays for each component: x1 x2 x3 x4 y1 y2 y3 y4 z1 z2 z3 z4 w1 w2 w3 w4 Generally, the preferred organizational method for vertecies is the former. Under SSE2, the compiler (or very unfortunate programmer) would have to reorganize the data during processing. """ The article is talking about how hadd and hsub in SSE3 help to corrects the situation, but SSE3 isn't yet nearly as ubiquitous as SSE/SSE2, I would imagine. --bbMikola Lysenko wrote:Seems it's great. It doesn't really matter what the underlying data is. An SSE instruction will add four 32-bit floats in parallel, nevermind whether the floats are x x x x or x y z w. What meaning the floats have is up to the programmer. Of course, channelwise operations will be faster in planer (EX: add 24 to all red values, don't spend time on the other channels), while pixelwise operations will be faster in interleaved (EX: alpha blending) - these facts don't have much to do with SIMD. Maybe the guy from intel wanted to help planar pixelwise operations (some mechanism to help the need to dereference 3-4 different places at once) or help interleaved channelwise operations (only operate on every fourth float in an array without having to do 4 mov/adds to fill a 128 bit register).Bill Baxter wrote:Ok. Well, I've never used any of these MMX/SSE/Altivec things myself, so it was just heresay. But the source was someone I know in the graphics group at Intel. I must have just misunderstood his gripe, in that case."Most CPUs today have *some* kind of SSE/Altivec type thing" That may be, but I've heard that at least SSE is really not that suited to many calculations -- especially ones in graphics. Something like you have to pack your data so that all the x components are together, and all y components together, and all z components together. Rather than the way everyone normally stores these things as xyz, xyz. Maybe Altivec, SSE2 and SSE3 fix that though. At any rate I think maybe Intel's finally getting tired of being laughed at for their graphics performance so things are probably changing.I have never heard of any SIMD architecture where vectors works that way. On SSE, Altivec or MMX the components for the vectors are always stored in contiguous memory.In terms of graphics, this is pretty much optimal. Most manipulations on vectors like rotations, normalization, cross product etc. require access to all components simultaneously. I honestly don't know why you would want to split each of them into separate buffers... Surely it is simpler to do something like this: x y z w x y z w x y z w ... vs. x x x x ... y y y y ... z z z z ... w w w ...Yep, I agree, but I thought that was exactly the gist of what this friend of mine was griping about. As I understood it at the time, he was complaining that the CPU instructions are good at planar layout x x x x y y y y ... but not interleaved x y x y x y. If that's not the case, then great. --bb
Jan 29 2007
A quite cool feature was something similair to Python's __getattr__, __setattr__ special functions. So if one writes class foo { ... // foo does not declare a function or member bar // special operators do not return anything opGetAttr(out T val, char[] identifier); opSetAttr(in T val, char[] identifier); }; void test() { foo test = new foo; test.bar = 5; assert( test.bar == 5 ); } The idea is, that the compiler implicitly translates the assignment/access to the undefined identifier bar to calls of opSetAttr and opGetAttr. Also functions should be be accessible this way, so a function call test.foobar(...) should be translated. Of course any valid function prototype must be made avaliable through propper op(S G)etAttr overloads. Eventually we could also think about op(G|S)etAttr being variadic or template functions. The result would be some kind of late linking capability. The main application I'm thinking of are scripting language bindings. One would have to write only one universal scripting object class, that would be instanced for every object to be accessed from D, being parameterized ro call the right interface functions. A call to say spam.eggs of a Python adapter class instance would be translated into opGetAttr(f, "eggs"), which would give back a function object, which call will result in the function calls of the Python API to call the function in the python interpreter. Wolfgang Draxinger -- E-Mail address works, Jabber: hexarith jabber.org, ICQ: 134682867
Jan 27 2007
Wolfgang Draxinger wrote:The main application I'm thinking of are scripting language bindings. One would have to write only one universal scripting object class, that would be instanced for every object to be accessed from D, being parameterized ro call the right interface functions. A call to say spam.eggs of a Python adapter class instance would be translated into opGetAttr(f, "eggs"), which would give back a function object, which call will result in the function calls of the Python API to call the function in the python interpreter.Have you seen Pyd, I wonder? http://pyd.dsource.org/ -- Kirk McDonald Pyd: Wrapping Python with D http://pyd.dsource.org
Jan 27 2007
Kirk McDonald wrote:Have you seen Pyd, I wonder? http://pyd.dsource.org/Yes I have, but I was more thinking of using Python modules from D. Having such an interface would be cool for other things, too. For example one could use this as a convenient interface to things like the OpenGL API. For my 3D engine wrapper classes and one is there for abstracting stuff like textures. Textures have several parameters which are set through glTexParameter and glTexEnv. Currently for each parameter there is a pair of property functions. But each OpenGL extension that extends the set of avaliable texture parameters requires to either extend, or to derive the texture class. However most texture parameters are numeric. The class could have a public avaliable AA, which maps property names to the OpenGL tokens. Then a universal handler function would use that array to get the OpenGL token from the requested member and perform the function calls. There would be a basic set of functions of course, but upon extension loading that extension wrapper could extend the maping AA apropriately. This way the texture class wouldn't have to be extended/derived, keeping the codebase small and consistent. Wolfgang Draxinger -- E-Mail address works, Jabber: hexarith jabber.org, ICQ: 134682867
Jan 28 2007
Wolfgang Draxinger wrote:A quite cool feature was something similair to Python's __getattr__, __setattr__ special functions. So if one writes class foo { ... // foo does not declare a function or member bar // special operators do not return anything opGetAttr(out T val, char[] identifier); opSetAttr(in T val, char[] identifier); };I think this could be a great feature, but be better as a template, because compile-time variables can be accessed at runtime, but not vice versa. Also remember that properties can mimic member variables, so supporting member functions should be enough. For any arbitrary call like foo.bar(a, b, c); the compiler would then follow the following law: if no member 'bar' is found that overloads satisfactorily, try rewriting it to foo.opFakeMember!("bar")(a, b, c); and see if a satisfactory overload can be found. This would allow more things, yet often keep the overhead restricted to compile time. For instance, you could use this to generate 'swizzling' functions, as discussed in the 'small vector' thread above, or generate conversions to all possible color spaces (rgba, bgra, etc): struct MyVector { template opFakeMember(char[] ident) { static if (ident[0..7] == "swizzle" && ident.length == 11) alias swizzle!(ident[7..11]).swizzle opFakeMember; } MyVector swizzle(char[] order)() { // Do some more template meta-programming to generate a swizzle } } And, having it as a template doesn't exclude transmitting the identifier at runtime: class MyDynamicPythonClass { void* opFakeMember(char[] ident)() { return runPythonLookupToGetThisVariable(ident); // Run at runtime } void opFakeMember(char[] ident)(void* foo) { setPythonVariable(ident, foo); // Run at runtime } unittest { auto test = new MyDynamicPythonClass(); test.bar = 5; // Property syntax allows variable mimicry; writeflln(test.boom); } } Cheers, Reiner
Jan 28 2007
Reiner Pope wrote:struct MyVector { template opFakeMember(char[] ident)I wouldn't call it "opFakeMember", since those members are not really fakes. How about "opVirt[ual]Member" or "opAttr[ibute]"?. Wolfgang Draxinger -- E-Mail address works, Jabber: hexarith jabber.org, ICQ: 134682867
Jan 29 2007
I think something like a 'super static' function could also be good (just to avoid using new keywords ;-) ). The idea would be that plain D code could be called by template functions, and the compiler would evaluate them at compile-time. As I see it, there are only a few requirements for this (which are recursive across all functions called): 1. the source code is available 2. there's no assembly or C (or any external library) called 3. the functions are pure (ie no static variables are read or changed, and the passed parameters are unchanged) This would mean, for instance, that practically all of std.string would be accessible via templates, avoiding the much of the need for separate meta-programming libraries. You could then just call such functions straight from your template code: import std.string; template foo(char[] bar) { static if (bar.splitlines()[0] == "cheese") pragma(msg, "You like cheese"); } The compiler can check whether a function can be evaluated at compile-time, and you may wish to ensure that yours is, since you are writing it for template code. In that case, you annotate your function 'super static' and the compiler will give you an error if it fails any of the three criteria. Cheers, Reiner
Jan 28 2007
Reiner Pope wrote:I think something like a 'super static' function could also be good (just to avoid using new keywords ;-) ). The idea would be that plain D code could be called by template functions, and the compiler would evaluate them at compile-time. As I see it, there are only a few requirements for this (which are recursive across all functions called): 1. the source code is available 2. there's no assembly or C (or any external library) called 3. the functions are pure (ie no static variables are read or changed, and the passed parameters are unchanged) This would mean, for instance, that practically all of std.string would be accessible via templates, avoiding the much of the need for separate meta-programming libraries. You could then just call such functions straight from your template code: import std.string; template foo(char[] bar) { static if (bar.splitlines()[0] == "cheese") pragma(msg, "You like cheese"); } The compiler can check whether a function can be evaluated at compile-time, and you may wish to ensure that yours is, since you are writing it for template code. In that case, you annotate your function 'super static' and the compiler will give you an error if it fails any of the three criteria. Cheers, ReinerAlternatively, if this is too hard to implement (since I think it effectively means writing a D interpreter within the compiler -- for the references only, since almost anything with value semantics already needs to be const-foldable for templates) another option could be the (slightly less cool) approach that Nemerle takes: precompile the library to a .dll file, and direct the compiler to that when it runs.
Jan 28 2007
Chad J Wrote:BCS wrote:I think it would be good to add a "property" keyword and maybe also "get" and "set". I think also would be nice to add a feature from ruby that i think works something like this: Ruby code: class C { attr_reader :variable1, :variable2, :variable3 } and this will create a private variable and a read property. And ther is also "attr_writer" to create a write property. In D this could look something like this D code: class C { property { int variable1; char[] variable2; int variable3; } } This would create a private variable and a get and set property method. Then you could also write like this: D code: class C { private property { int variable1; char[] variable2; int variable3; } } to make the get and set property methods private. You could write like this: D code: class C { get { int variable1; char[] variable2; int variable3; } } to only make a private variable and a get property method. The get and set methods would be public as default(i think) and the variable would always be private. I think it would be great if you could write as i have described above because when you want a private variable and get and set methods that only sets or returns the value. If you would like to do something in the methods it could look like this: D code: class C { private int variable_; public get int variable () { return variable_; } public set int variable (int variable) { if (variable > 3) return variable_ = variable; } }Now that the big 1.0 is out, I think that we should start considering what to put into 2.0. I don't think we need to rush anything into D yet, but maybe we should start accumulating a list of things to consider. One overall thought I had is that it seems the big killer for C++ was to some extent that it is a strict superset of C. Maybe we should avoid this problem and not requiter that D2.0 be backwards compatible at the source level, but only at the ABI level sort of like C and D are now. Anyway, just a thought.... The first thing that comes to my mind is explicit properties. I believe they can be done without using new keywords or sacrificing much generality. Eventually the implicit properties could be deprecated, when everyone feels they are no longer widely used.
Feb 05 2007
Reply to doob,I think it would be good to add a "property" keyword and maybe also "get" and "set". I think also would be nice to add a feature from ruby that i think works something like this:befor I'd go for that it rather see const aliases class C { private int foo; const alias Foo; } C c = new c; auto cf = c.Foo; // valid c.Foo = 2; // invalid auto cf = &c.Foo; // invlaid That covers the get side, and what use is the set side if you don't provide code? (Overriding maybe??) I /like/ the property syntax D uses and would hate to see it discarded.
Feb 05 2007