www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - seeding the pot for 2.0 features

reply BCS <ao pathlink.com> writes:
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);
	}
}

3) promote static foreach to the same level as static if. This with #2 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.
Jan 23 2007
next sibling parent reply kris <foo bar.com> writes:
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);
     }
 }
 
 3) promote static foreach to the same level as static if. This with #2 
 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
parent reply BCS <ao pathlink.com> writes:
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
parent kris <foo bar.com> writes:
BCS wrote:
 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.

Yeah, please forgive me for being a bit flippant; I really didn't get #2, so examples are helpful :)
Jan 23 2007
prev sibling next sibling parent reply Pragma <ericanderton yahoo.removeme.com> writes:
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.
 
 3) promote static foreach to the same level as static if. This with #2 
 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
parent reply BCS <ao pathlink.com> writes:
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 else

magic! {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?
 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.
 


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)
 
 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
parent reply "Kristian Kilpi" <kjkilpi gmail.com> writes:
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=

 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
parent reply BCS <BCS pathlink.com> writes:
Kristian Kilpi wrote:
 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_ = 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_;}

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.
Jan 25 2007
parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Thu, 25 Jan 2007 20:08:29 +0200, BCS <BCS pathlink.com> wrote:
 Kristian Kilpi wrote:
 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=



 m){return ((i_ * m)+j_)*m +k_;}



 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=


 {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;}

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.
Jan 27 2007
prev sibling next sibling parent Chad J <gamerChad _spamIsBad_gmail.com> writes:
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
prev sibling next sibling parent BCS <BCS pathlink.com> writes:
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
prev sibling next sibling parent reply Mikola Lysenko <mclysenk mtu.edu> writes:
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
next sibling parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
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.
 
 
 
 -Mik

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).
Jan 28 2007
parent Mikola Lysenko <mclysenk mtu.edu> writes:
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
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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 Simulation

It'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
next sibling parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
Bill Baxter wrote:
 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 Simulation

It'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.

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.
 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

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.
Jan 28 2007
next sibling parent Mikola Lysenko <mclysenk mtu.edu> writes:
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
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Chad J wrote:
 Bill Baxter wrote:
 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 Simulation

It'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.

What applications don't use vector instructions?

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.
 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
prev sibling parent reply Mikola Lysenko <mclysenk mtu.edu> writes:
Bill Baxter wrote:
 Mikola Lysenko wrote:
 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 Simulation

It'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.

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.
 
 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.

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 -Mik
Jan 28 2007
next sibling parent reply "Joel C. Salomon" <JoelCSalomon Gmail.com> writes:
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.vecmath

Considering 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
next sibling parent reply Mikola Lysenko <mclysenk mtu.edu> writes:
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
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Mikola Lysenko wrote:
 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.

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.
Jan 29 2007
parent reply Mikola Lysenko <mclysenk mtu.edu> writes:
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
parent Walter Bright <newshound digitalmars.com> writes:
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
prev sibling parent reply "Joel C. Salomon" <JoelCSalomon Gmail.com> writes:
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
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Joel C. Salomon wrote:
 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.”

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.
Jan 30 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Joel C. Salomon wrote:

 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.

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 --bb
Jan 29 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Mikola Lysenko wrote:
 Bill Baxter wrote:
 Mikola Lysenko wrote:
 2. Low dimensional vectors as primitive types



"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. --bb
Jan 28 2007
next sibling parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
Bill Baxter wrote:
 Mikola Lysenko wrote:
 
 Bill Baxter wrote:

 Mikola Lysenko wrote:

 2. Low dimensional vectors as primitive types



"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.

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.
 
 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.
 
 --bb

Jan 28 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Chad J wrote:
 Bill Baxter wrote:
 Mikola Lysenko wrote:

 Bill Baxter wrote:

 Mikola Lysenko wrote:

 2. Low dimensional vectors as primitive types





      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.

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.

Agreed. 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. --bb
Jan 28 2007
parent Chad J <gamerChad _spamIsBad_gmail.com> writes:
Bill Baxter wrote:
 Chad J wrote:
 
 Bill Baxter wrote:

 Mikola Lysenko wrote:

 Bill Baxter wrote:

 Mikola Lysenko wrote:

 2. Low dimensional vectors as primitive types




[...]

      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.

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.

Agreed. 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. --bb

*sigh* but I want my uber vector operations NOW! :)
Jan 28 2007
prev sibling parent reply Mikola Lysenko <mclysenk mtu.edu> writes:
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
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Mikola Lysenko wrote:
 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.

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.
 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
parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
Bill Baxter wrote:
 Mikola Lysenko wrote:
 
 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.

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.
 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

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).
Jan 29 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Chad J wrote:
 Bill Baxter wrote:
 Mikola Lysenko wrote:

 Bill Baxter wrote:

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

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).

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/ --bb
Jan 29 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Chad J wrote:
 Bill Baxter wrote:
 Mikola Lysenko wrote:

 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.

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.
 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

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).

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. --bb
Jan 29 2007
parent Chad J <gamerChad _spamIsBad_gmail.com> writes:
Bill Baxter wrote:
 Chad J wrote:
 
 Bill Baxter wrote:

 Mikola Lysenko wrote:

 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.

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.
 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

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).

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. --bb

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.htm
Jan 29 2007
prev sibling next sibling parent reply Wolfgang Draxinger <wdraxinger darkstargames.de> writes:
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
next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
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
parent Wolfgang Draxinger <wdraxinger darkstargames.de> writes:
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
prev sibling parent reply Reiner Pope <xxxx xxx.xxx> writes:
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);
 };

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
parent Wolfgang Draxinger <wdraxinger darkstargames.de> writes:
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
prev sibling parent reply Reiner Pope <xxxx xxx.xxx> writes:
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
parent Reiner Pope <xxxx xxx.xxx> writes:
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,
 
 Reiner

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