* * *

Author Topic: Using operators for in-place operations  (Read 1684 times)

MountainQ

  • New member
  • *
  • Posts: 16
Using operators for in-place operations
« on: May 16, 2017, 01:29:52 pm »
Hi everybody,
I have a question that quite naturally comes up implementing operators on arrays; the following example should be illustrative:

Code: Pascal  [Select]
  1. type
  2.   TIA: array of integer;
  3. ...
  4.  
  5. operator * (vect1: TIA; sc: integer) vect2: TIA;
  6. var
  7.   i: integer;
  8. begin
  9.   SetLength(vect2, Length(vect1));
  10.   for i := 0 to Length(vect1)-1 do
  11.     vect2[i] := sc*vect1[i];
  12. end;
  13.  
  14. var
  15.   l: TIA;
  16. ...
  17. l *= 2
  18.  

Using operators the however poses a problem here: the operator is built like a function. This means it will create a new array even in the case the result is stored in the existing one. (No in-place operation)
In addition this also implies that a more complicated expression like ...
Code: Pascal  [Select]
  1. var
  2.   l1, l2: TIA;
  3. ...
  4. l2 := cos(l1*3+4)
  5.  
... might internally lead to a dummy array being created to store l1*3 then the next dummy array containing l1*3+4 and so on.
I checked the adresses of vect1, vect2 in the above example, confirming that these dummy arrays are utilized.
Is there a way around such that the code is still readable and the underlying operation remains efficient?
And apologies in case this topic has already been adressed; I did not find it.

Thaddy

  • Hero Member
  • *****
  • Posts: 4779
Re: Using operators for in-place operations
« Reply #1 on: May 16, 2017, 05:32:17 pm »
That's complete nonsense.

If you define an operator you have full control.
It will do anything you say and if you want it in place it will be in place. And or can be declared inline.
What's *inside* the body of the operator can all be done with pointer references.

Lack of skills... <Ok grumpy mode on: >:D >:D >:D>

Try my approach. You will see it works... :)

Note that doing it in place with operators is completely bollocks but at least it is your code, not mine (Fired! >:D also in any other language capability: stupid code)
« Last Edit: May 16, 2017, 05:46:10 pm by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

cpicanco

  • Sr. Member
  • ****
  • Posts: 414
    • My Portfolio
Re: Using operators for in-place operations
« Reply #2 on: May 16, 2017, 07:04:30 pm »
Come on Thaddy, he is a Newbie. Give him a chance!

Thaddy

  • Hero Member
  • *****
  • Posts: 4779
Re: Using operators for in-place operations
« Reply #3 on: May 16, 2017, 08:31:30 pm »
Ok... ;) :-[
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

MountainQ

  • New member
  • *
  • Posts: 16
Re: Using operators for in-place operations
« Reply #4 on: May 17, 2017, 10:29:32 am »
Oh excuse me, I was left under the impression this is a forum where questions should be asked.
I can understand that questions thar are posed in a non-ideal way may annoy people, but it would still be nice to get a reasonable response.
Anyway, I don't see the question answered, therefore I will try and rephrase it:
what is the correct way to implement operators that act on arrays; my concern still is that it leads to a code that is very readable, yet inefficient.
My reasoning for this is sketched in the above example, in short a naive implementation leads to the avoidable creation of temporary arrays.
If operators are an inherently bad starting poinnt, is there a reference or guideline other then
https://www.freepascal.org/docs-html/ref/refch15.html

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 5875
Re: Using operators for in-place operations
« Reply #5 on: May 17, 2017, 11:29:32 am »
Ignore Thaddy, we do too  ;)

Afaik such operators don't exist at the moment. It would double the number of operators for a new type.

In case of simple types, inlining the operator might give the compiler a chance to figure it out, but the example you show is too complicated to have hopes for that.

sky_khan

  • Guest
Re: Using operators for in-place operations
« Reply #6 on: May 17, 2017, 12:15:10 pm »

Thaddy is the reason why I decided to do what is written in my signature. FYI, You can find "ignore list" in Profile-Modify Profile-Buddys/Ignore List :)
Do it before its too late. lol

MountainQ

  • New member
  • *
  • Posts: 16
Re: Using operators for in-place operations
« Reply #7 on: May 17, 2017, 12:24:37 pm »
Thanks guys for your kind response.
Luckily, this operator declaration is not something I need terribly, yet it would of course be awesome. Still, does anybody know some guidelines or documentation about the internals of an operator; other than the above link.
All the best.

ASerge

  • Sr. Member
  • ****
  • Posts: 472
Re: Using operators for in-place operations
« Reply #8 on: May 17, 2017, 10:15:41 pm »
Is there a way around such that the code is still readable and the underlying operation remains efficient?
Use a separate function implemented effectively. In my opinion this will be more readable than the use of operators.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 4415
    • wiki
Re: Using operators for in-place operations
« Reply #9 on: May 18, 2017, 02:14:23 am »
You will not be able to do it with a function or operator (since an operator is a function).
This is because neither of the 2 gives you access to the var that will receive the result. So you can not check if the variable that will receive the  result  is the same as the source.

Of course you can write a function/operator that always overwrites the source. But that is probably not what you want, since "arr1 := arr2 * 3" should not modify arr2.


You can do it with a procedure, using var params.
Code: Pascal  [Select]
  1. procedure arr_mul(var src: TArr; factor: integer; var dst: TArr);
  2. begin
  3.   SetLength(dst, length(src));
  4.   for i := ...
  5.      dst[i] := src[i]* factor;
  6. end;
  7.  

If you pass in the same for src and dst, then the operation is done in place.

Note: the SetLength is still needed, even if src and dst are the same.
Code: Pascal  [Select]
  1.  a2:= a1; // both point to same memory; only inc ref count
  2.  arr_mul(a1, 3, a1);
  3.  
In the above a2 should NOT be modified.
The SetLenght, as detects, that dst has a ref count > 1 and copies it first, so it no longer shares the memory of a2.


however
Code: Pascal  [Select]
  1.  a2:= a1;
  2.  arr_mul(a2, 3, a1);
  3.  
even so a2 and a1 point to the same memory, this will make a new copy, and leave a1 un-modified.
But that is actually good, since a1 may get used later....

MountainQ

  • New member
  • *
  • Posts: 16
Re: Using operators for in-place operations
« Reply #10 on: May 18, 2017, 10:17:20 am »
One more time thanks for your replies; I am aware of the possibility of writing procedure that can overwrite the 'source/dest'.
However in a more complicated expression the resulting expression would quickly become ugly. Certainly writing a loop is clearer at that point.
Quote
This is because neither of the 2 gives you access to the var that will receive the result. So you can not check if the variable that will receive the  result  is the same as the source.
I aggree, as long as there is no access to the result, I cannot think of a way of creating efficient operators (in the sense that no additional memory needs to be allocated).

MountainQ

  • New member
  • *
  • Posts: 16
Re: Using operators for in-place operations
« Reply #11 on: May 18, 2017, 12:40:51 pm »
Hi everyone, one more thought from my part:

The idea is that operators do not actually perform any mathematical operation; their call merely builds up the operation that is executed when a 'final' procedure is called. That requires in addition to the data the storage of a list of mathematical operations; it could look like this:
Code: Pascal  [Select]
  1. type
  2.   TCalc = function(const a, b: Extended): Extended;
  3.   TCalcR = record
  4.     f: TCalc;
  5.     x: extended;
  6.     p: ^TAA;
  7.   end;      
  8.   TAA = class
  9.   private
  10.     fcts: array of TCalcR;
  11.   public
  12.     data: TFA;      
  13.     ...
  14.  
Now an operator needs to provide information what should be carried out later on (e.g. addition):
Code: Pascal  [Select]
  1. function _add(const x0, x1: extended): extended;
  2. begin
  3.   Result := x0+x1;
  4. end;
  5.  
  6. operator + (vect1: TAA; sc: extended) vect2 : TAA;
  7. var
  8.   n: integer;
  9. begin
  10.   n := Length(vect1.fcts);
  11.   SetLength(vect1.fcts, n+1);
  12.   vect1.fcts[n] := CalcR(@_add, sc, nil);
  13.   vect2 := vect1;
  14. end;  
  15.  
And finally a function needs to actually perform the calculation:
Code: Pascal  [Select]
  1. procedure calc(var dest: TAA; source: TAA);
  2. var
  3.   n, m, j, i: integer;
  4.   c: extended;
  5. begin
  6.   n := Length(source.data);
  7.   m := Length(source.fcts);
  8.   // initialize dest if necessary
  9.   for i := 0 to n-1 do
  10.   begin
  11.     c := calcf(i, source.data[i], source.fcts[0]);
  12.     for j := 1 to m-1 do
  13.       c := calcf(i, c, source.fcts[j]);
  14.     dest.data[i] := c;
  15.   end;
  16.   SetLength(source.fcts, 0);
  17. end;  
  18.  

So that finally a reasonbly simply looking code can be used when performing arithmetic operations on arrays without creating much memory overhead.
Code: Pascal  [Select]
  1. var
  2.   a1, a2: TAA;  
  3. ...
  4.   // init a1
  5.   calc(a2, (a1+3)+3);
  6.  
However, a for loop is (not surprisingly) much faster; for simple operations almost a factor of 10. For more complex stuff such as exp() this probably would look somewhat better. This also works when an operations incorporates two arrays.
Hopefully someone finds this interesting and all the best.

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus