tags:

views:

211

answers:

2

What is the more efficient way?

FUserRecords[I].CurrentInput:=FUserRecords[I].CurrentInput+typedWords;

or

var userRec: TUserRec;
...
userRec:=FUserRecords[I];
userRec.CurrentInput:=userRec.CurrentInput+typedWords;
FUserRecords[I]:=userRec;
+8  A: 

In the case you have described, the first example will be the most efficient.

Records are value types, so when you do this line:

userRec:=FUserRecords[I];

You are in fact copying the contents of the record in the array into the local variable. And when you do the reverse, you are copying the information again. If you are iterating over the array, this can end up being quite slow if your array and the records are quite large.

If you want to go down the second path, to speed things up you can manipulate the record in the array directly using a pointer to the record, like this:

type
  TUserRec = record
    CurrentInput: string;
  end;
  PUserRec = ^TUserRec;

var
  LUserRec: PUserRec;
...
LUserRec := @FUserRecords[I];
LUserRec^.CurrentInput := LUserRec^.CurrentInput + typedWords;

(as discussed in the comments, the carets (^) are optional. I only added them here for completeness).

Of course I should point out that you really only need to do this if there is in fact a performance problem. It is always good to profile your application before you hand code these sorts of optimisations.

EDIT: One caveat to all the discussion on the performance issue you are looking at is that if most of your data in the records are strings, then most of any performance lost in the example you have shown will be in the string concatenation, not in the record manipulation.

Also, the strings are basically stored in the record as pointers, so the record will actually be the size of any integers etc, plus the size of the pointers for the strings. So, when you concatenate to the string, it will no increase the record size. You can basically look at the string manipulation part of your code as a separate issue to the record manipulation.

You have mentioned in your comments below, this is for storing input from multiple keyboards, I can't imagine that you will have more than 5-10 items in the array. At that size, doing these steps to optimize the code is not really going to boost the speed that much.

I believe that both your first example and the pointer-using code above will end up with approximately the same performance. You should use which ever is the easiest for you to read and understand.

N@

Nat
oops. Yeah, I forgot to put the last line. Ok I'll try that.Are pointers more efficient?
Dian
Yep, a pointer would be more efficient *in this case*. I would also point [sic] out that the dereference operator is entirely optional in Delphi. LUserRec^.xyz can simply be written LUserRec.xyz and be automatically dereferenced. For those aghast at the "ambiguity" introduced by such short-cuts, what else could it possibly mean? And note that we do this ALL THE TIME with object reference variables (which are pointers by another name).
Deltics
Yes, pointers will be much more efficient, because you wont be copying data around. I will change my answer to reflect the change in your question.
Nat
Using the first option is actually the only option. As Nat pointed out records are "values", you don't want to copy them. Implementing Option 2 with pointers would simply obfuscate the code and provide no benefit (might even slow things down a tiny bit). When you're working with array Delphi will usually compute the offset to the desired field and use "based indexed addressing mode": that's one assembler instruction to read/write a word-sized field. If you replace that with explicit pointers you'll use a minimum of two instructions + 1 extra register to do the same job.
Cosmin Prund
@Deltics: but P1 := P2 is not the same than P1^ := P2^, why should dereferencing be optional when accessing a field? The fact that Delphi made that operator optional is IMHO a bug (and I filed it in QC), and can only lead to confusion.Object references are handled in a different way, you can't dereference them anyway, they can be used in one way only, they are not "pointer" type. For the matter even strings and dynamic arrays are implemented with pointers, var variables are implemented with pointers, but the are not "pointer" types. Delphi should stick to its own rules to avoid confusion.
ldsandon
@ldsandon: Yes, P1 := P2 <> P1^ := P2^... but P1.xyz := P2.xyz makes absolutely no sense and should not compile. To make it compile you would HAVE to add ^ to dereference. But we don't have to do that then the "P" is an obfuscated pointer (i.e. an object reference) so why should we have to do it when it isn't obfuscated? It's entirely unnecessary and doesn't lead to confusion or ambiguity. You are yourself confused... you find it entirely reasonable to let Delphi take care of any number of automatic dereferencing cases for you that you mention, but then complain at this one case?
Deltics
Deltics, almost anything is a pointer, if you like. The fact that an explicit pointer type *is different* than a type which is implemented internally using pointers should be obvious. Strings, arrays and object instances types *are not pointers types*, and in fact you can't dereference them. For the matter, even var parameters are pointers at the lowest level and you never dereference them anyway.You are confused because you like the shortcut, but looking at it from the perspective of language design and cleanness, that's a very bad and confusing feature.
ldsandon
I am confused now, Cosmin says pointers might not be beneficial. While Nat and Deltics said it would be more efficient. So which is better my first option? or the pointer option?
Dian
Basically, the first option and the second option with pointers will end up being about the same speed. Delphi will be doing the same sorts of pointer management underneath your code in the first option. I don't agree with Cosmin's comment that 'it might even slow things down a tiny bit'. What size is your array anyway? And is the only thing in the record a string, or is there more data?
Nat
The size of the array depends on how many keyboards are connected to the computer (since my application is multi-keyboard/multi-user). TUserRec has UserName, CurrentInput (as string fields) and Score, CurrentNo (as Integer Fields). However, in a method, I only deal with a field or two, except maybe the first method (create form) where I initialize all fields.
Dian
I have updated my answer in light of your comments... Can I ask if you have done any performance testing? If there is a performance problem, I would think it is the concatenation of the strings, not the record manipulation.
Nat
No, I haven't. I just wanted to optimize everything because I have this IME-related workaround that is the most messy/inefficient code I have ever written and somehow I wanted to make the rest of the code awesome to make up for that shortcoming. Thank you for all your help, I appreciate it. :D
Dian
+1  A: 

Faster and more readable:

with FUserRecords[I] do 
  CurrentInput := CurrentInput + typedWords;
dmajkic
Why would I add the record's `typedWords` field to the `CurrentInput` field of that same record?
Rob Kennedy
@Rob I have no idea, but code in question does that.
dmajkic
Gotcha! The code in the question *doesn't* do that, which calls into question your claim that your code is more readable. Also, please show that your code is faster.
Rob Kennedy
FUserRecords[I].CurrentInput:=FUserRecords[I].CurrentInput+typedWords is the code. "with FUserRecords[I]" will index (I) and dereference (.) only once, if not optimized by compiler. Code below that (in question) will assign record to local variable, change it, and then assign it back.
dmajkic
That is more readable. So I guess I'll be using it for some parts of my code. Thank you for answering, I appreciate it.
Dian
@Rob: I see. You think that there is potential trouble in that record itself has field named typedWords, and that it will make wrong results. I must say that after working with ruby, and sticking to naming conventions, I find "with" very readable and useful.
dmajkic
Oh, I see the problem Rob has spotted. The typedWords could be possibly mistaken as a field of the record (it's not supposed to be a record field, it's a global variable). But the code works fine anyway.One question, what if the record has a field named typedWords, will it use the value of the global variable or the value of the field?
Dian
This is where `with` is dangerous. It will use the `typedWords` in the record, not the global. This is the only reason I didn't put this solution in my answer... Oh, and that I didn't want to be flamed by anti-with-ites!
Nat