Array operations -- not actually so difficult

Bill Baxter wbaxter at gmail.com
Fri Dec 15 11:50:08 PST 2006


Sean Kelly wrote:
> Bill Baxter wrote:
> 
>> Don Clugston wrote:
>>
>>> Array operations are one of the very few 2.0 features that haven't 
>>> made it into 1.0. My experiments into expression templates suggest 
>>> that it might not be very complicated after all.
>>>
>>> Consider something like a dot product,
>>>
>>> real a[], b[], c[];
>>> real d = dot(a+b, c);
>>>
>>> For performance, it would be better if the compiler actually *didn't* 
>>> evaluate a+b. Instead, that should compile to:
>>>
>>> real sum = 0;
>>> for (int i=0; i<c.length; ++i) {
>>>      sum+=(a[i]+b[i])*c[i];
>>> }
>>> d=sum;
>>>
>>> In fact, we could create a fantastic library implementation of array 
>>> operations with a tiny bit of syntactic sugar:
>>> instead of generating an error message for array operations, look for 
>>> an opXXX function, and treat it the same way the implicit array 
>>> properties work.
>>
>>
>> How does that result in the above code for dot product?
>> Looks to me like if all you have is calling opXXX functions the above 
>> dot would still become two loops with a tmp for the a+b.
> 
> 
> Not necessarily.  Consider:
> 
>     struct AddExp(T, U)
>     {
>         T lhs;
>         U rhs;
> 
>         static AddExp!(T,U) opCall( T lhs, U rhs )
>         in
>         {
>             assert( lhs.length == rhs.length );
>             assert( is( typeof(T[0]) == typeof(U[0]) ) );
>         }
>         body
>         {
>             AddExp!(T,U) exp;
>             exp.lhs = lhs;
>             exp.rhs = rhs;
>             return exp;
>         }
> 
>         typeof(T[0]) opIndex( size_t i )
>         {
>             return lhs[i] + rhs[i];
>         }
> 
>         typeof(T[0])[] opSlice( size_t b, size_t e )
>         {
>             typeof(T[0])[] sum = new typeof(T[0])[lhs.length];
> 
>             for( size_t i = 0; i < lhs.length; ++i )
>             {
>                 sum[i] = lhs[i] + rhs[i];
>             }
>             return sum;
>         }
> 
>         size_t length()
>         {
>             return lhs.length;
>         }
>     }
> 
> 
>     AddExp!(T,U) opAdd(T, U)( T lhs, U rhs )
>     {
>         return AddExp!(T,U)( lhs, rhs );
>     }
> 
> 
>     T[] opAssign(T, U)( inout T[] lhs, U rhs )
>     in
>     {
>         assert( is( typeof(T[0]) == typeof(U[0]) ) );
>     }
>     body
>     {
>        lhs = rhs[0 .. $];
>        return lhs;
>     }
> 
> 
>     real dotp(T,U)( T lhs, U rhs )
>     {
>         real sum = 0.0;
>         assert( lhs.length == rhs.length );
>         for( size_t i = 0; i < lhs.length; ++i )
>         {
>             sum += lhs[i] * rhs[i];
>         }
>         return sum;
>     }
> 
> 
>     void main()
>     {
>         real[] a, b, c;
>         a ~= 1.0; a ~= 2.0;
>         b ~= 3.0; b ~= 4.0;
>         c ~= 5.0; c ~= 6.0;
> 
>         // test opSlice
>         auto   s = opAdd( a, b );
>         real[] r = s[0 .. s.length];
> 
>         for( size_t i = 0; i < r.length; ++i )
>         {
>             printf( "%Lf = %Lf\n", r[i], s[i] );
>         }
> 
>         // compound expression
>         real res = dotp( opAdd( a, b ), c );
> 
>         printf( "\n%Lf\n", res );
>     }
> 
> prints:
> 
>     4.000000 = 4.000000
>     6.000000 = 6.000000
> 
>     56.000000
> 
> Thanks to the wonder of templates, a properly designed expression struct 
> could be evaluated as-is rather than needing to be converted to an array 
> before being passed to dotp().  If we were able to define free-floating 
> operators as per Don's suggestion, the above would be darn slick :-)
> 
> By the way, I originally tried slicing using the standard syntax "s[0 .. 
> $]" and got this error:
> 
>     test.d(84): Error: undefined identifier __dollar
> 
> I imagine this is a known issue?
> 
> 
> Sean

I put it on my D Rants page[1], but I don't think I ever filed it as a bug.

[1] http://www.prowiki.org/wiki4d/wiki.cgi?BillBaxter

--bb



More information about the Digitalmars-d mailing list