Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-01

for .. in loop implementation

18 messages · 11 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

leledumbo

OK, I've read http://wiki.freepascal.org/Modernised_Pascal this (and the FAQ as well). I disagree with any statement saying that for .. in loop is only a type-saver. It's a good language extension and should be included (since Delphi already have this, it will also be a good idea). Consider the following example:

type
  TDays: (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
  TDaySet: set of TDays;
var
  d: TDaySet;
begin
  for d in [Monday,Wednesday,Friday] do ;
  // versus
  for d:=Low(TDaySet) to High(TDaySet) do
    if d in [Monday,Wednesday,Friday] then ;
end;

The latter one has iteration overheads, while the former can be optimized to loop as many as needed. I'm not saying I'm the best Pascal programmer, but in case there's a (better) solution to this (rather than extending the language) please tell me.

View this message in context: http://www.nabble.com/for-..-in-loop-implementation-tp21327686p21327686.html Sent from the Free Pascal - General mailing list .
Marco van de Voort
to leledumbo

In our previous episode, leledumbo said:

The latter one has iteration overheads, while the former can be optimized to loop as many as needed. I'm not saying I'm the best Pascal programmer, but in case there's a (better) solution to this (rather than extending the language) please tell me.

Your assesment is correct as far as I can see.

However IMHO that doesn't meant you are right. One single case of an optimalization advantage, and then in a border case like iteration over a constant set does not justify a language extension.

Vincent Snijders
to leledumbo: OK, I've read http://wiki.freepascal.org/Modernised_Pascal this (and the FAQ as well).…
leledumbo wrote 24 lines
OK, I've read http://wiki.freepascal.org/Modernised_Pascal this (and the FAQ as well). I disagree with any statement saying that for .. in loop is only a type-saver. It's a good language extension and should be included (since Delphi already have this, it will also be a good idea). Consider the following example: type TDays: (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday); TDaySet: set of TDays; var d: TDaySet; begin for d in [Monday,Wednesday,Friday] do ; // versus for d:=Low(TDaySet) to High(TDaySet) do if d in [Monday,Wednesday,Friday] then ; end; The latter one has iteration overheads, while the former can be optimized to loop as many as needed. I'm not saying I'm the best Pascal programmer, but in case there's a (better) solution to this (rather than extending the language) please tell me.

You could write it like this:

type
   TDays: (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
   TDaySet: set of TDays;
var
   d: TDays; // note the type
   i: 0..2;
const
   SomeDays: array[0..2] of TDays = (Monday,Wednesday,Friday);
begin
   for i:=Low(SomeDays) to High(SomeDays) do
   begin
     d := SubSet[i];
   end;
end;

And it loops as many times as necessary.

Vincent

Jürgen Hestermann
to leledumbo: OK, I've read http://wiki.freepascal.org/Modernised_Pascal this (and the FAQ as well).…
Show quoted text 12 lines
I disagree with any statement saying that for .. in loop is only a type-saver. It's a good language extension and should be included (since Delphi already have this, it will also be a good idea). Consider the following example: for d in [Monday,Wednesday,Friday] do ; // versus for d:=Low(TDaySet) to High(TDaySet) do if d in [Monday,Wednesday,Friday] then ; The latter one has iteration overheads, while the former can be optimized to loop as many as needed. I'm not saying I'm the best Pascal programmer, but in case there's a (better) solution to this (rather than extending the language) please tell me.

I think there will be no performance difference because internaly the compiler has to generate something like the first for-loop anyway. It will only be hidden from the programmer and I am not sure whether this a good thing.

Everything that obscures what's going on under the hood has the potential to generate performance problems. In your example, you may not be aware that at the end all possible members have to be checked for beeing part of the set. If you only read

for d in [Monday,Wednesday,Friday] do ;

you may think that the loop is run through only 3 times, but internally there are much more iterations (to check the "if d in [Monday,Wednesday,Friday]").

Jürgen Hestermann.

Vinzent Höfler
to Jürgen Hestermann
Jürgen Hestermann wrote 18 lines
> I disagree with any statement saying that for .. in loop is > only a type-saver. It's a good language extension and should be included > (since Delphi already have this, it will also be a good idea). > Consider the > following example: > for d in [Monday,Wednesday,Friday] do ; > // versus > for d:=Low(TDaySet) to High(TDaySet) do > if d in [Monday,Wednesday,Friday] then ; > The latter one has iteration overheads, while the former can be > optimized to > loop as many as needed. I'm not saying I'm the best Pascal programmer, > but > in case there's a (better) solution to this (rather than extending the > language) please tell me. I think there will be no performance difference because internaly the compiler has to generate something like the first for-loop anyway.

No. It could unroll the loop, and then use constants throughout the three copied loop-bodies instead. Of course, a smart compiler could end up with the same approach after analyzing the code, so indeed: There should be no performance difference.

Show quoted text 6 lines
It will only be hidden from the programmer and I am not sure whether this a good thing. Everything that obscures what's going on under the hood has the potential to generate performance problems.

Yes, that's what I hear from C-enthusiasts all the time. And that's also why everything should be written in assembler these days, because then performance problems would jump right at you when reading the code.

(In case anyone wonders, yes, the last sentence above is 100% sarcasm.)

Show quoted text 9 lines
In your example, you may not be aware that at the end all possible members have to be checked for beeing part of the set. If you only read for d in [Monday,Wednesday,Friday] do ; you may think that the loop is run through only 3 times, but internally there are much more iterations (to check the "if d in [Monday,Wednesday,Friday]").

Indeed. But there could even be no loop at all, even if there's a loop expression in the source code.

Vinzent.

P.S. Honestly, Pascal (and especially ObjectPascal) already hides quite some stuff from the programmer. Properties, for example, can hide potentially costly procedure calls in simple assignment statements.

Things like

SomeStringList['Name'] := 'Value';

might be a relatively cheap O(1) operation if implemented with a hash table, it could be an O(log n) operation if the list is sorted and a binary search is done, or it could even be a O(n) operation if implemented by a simple linear search, but who knows? Some of the time you don't even care, because the number of strings is not large enough to make a noticeably difference; in fact, the usage of AnsiStrings alone and the associated reference counting and constant memory allocation and deallocation might even be much more influencing on the overall performance.

Don't get me wrong, usually this hiding of implementation details is a good thing, because it let's you solve the problem at hand a lot more quicker. If the solution turns out to be "not fast enough", you can still start optimizing instead of doing it the other way around.

Mantra: First make it work, then make it fast.

IOW: A fast, but wrong solution is one thing first: Wrong. No matter how fast it does it wrong.

BTW, any performance differences in the example above highly depends on the work done in the loop body anyway, so whining about possible performance issues is quite a bit premature here. ;)

dmitry boyarintsev
to Vinzent Höfler

the nice thing about pascal, is that compile support different code compilers syntax: {$mode ...}

if anyone likes, he/she can implement additional {$mode} for the compiler, right? this new {$mode} can be included into compiler packages, and if necesssary anyone can rebuild the compiler to support this new mode!

there're a lot of threads talking about "it's so cool to have this feature...". But fpc is open source, so feel free to implement it your own modes :)

anyway, about topic:

type
 TDays: (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
 TDaySet: set of TDays;
var
 d: TDaySet;
begin
 for d in [Monday,Wednesday,Friday] do ;
 // versus
 for d:=Low(TDaySet) to High(TDaySet) do
   if d in [Monday,Wednesday,Friday] then ;
end;

here's another nice way to implement this without overhead: open-arrays!

procedure DayLoop(const Days: array of TDays);
var
  i : integer;
begin
  for i := 0 to length(Days)  - 1 do
    Days[i]...
end;

...
DayLoop( [Monday,Wednesday,Friday] );
..

Jürgen Hestermann
to Vinzent Höfler: No. It could unroll the loop, and then use constants throughout the three copied…
P.S. Honestly, Pascal (and especially ObjectPascal) already hides quite some stuff from the programmer. Properties, for example, can hide potentially costly procedure calls in simple assignment statements.

That's true. But I am not happy about that approach. In the last years lots of things were added to Pascal that are no longer clear and simple. And most of them are of little use (for the programmer). They only bloated the so small and easy to learn language.

Mantra: First make it work, then make it fast.

In general that's true from the programmer's viewpoint. But this does not apply to adding language details because there is no 'first make it work'. Why obscure important implementation details if the only benefit is saving some writing?

BTW, any performance differences in the example above highly depends on the work done in the loop body anyway, so whining about possible performance issues is quite a bit premature here. ;)

Maybe, but you started talking about performance. ;-)

Jürgen Hestermann.

Vinzent Höfler
to Jürgen Hestermann
Jürgen Hestermann wrote 6 lines
> Mantra: First make it work, then make it fast. In general that's true from the programmer's viewpoint. But this does not apply to adding language details because there is no 'first make it work'. Why obscure important implementation details if the only benefit is saving some writing?

I won't judge on the "only save some writing" here, but generally it *does* apply to language features: If a particular language feature helps you writing correct code faster (by supporting you to make it work instead of relying on your abilities to use the debugger), then it does apply.

The real question is if the (or any other) proposal is "good enough" to do just that, or if it's really some syntactic sweetener (not even sugar, sugar at least contains energy). That's something to discuss.

Let me take a rather extreme point of view: After all, all those for-, while-, and repeat-until-loops are only there to save you from some typing work[0], because Pascal already has a perfectly good loop-construct with which you can do all that: goto. Would you agree here? Probably not.

Show quoted text 5 lines
> BTW, any performance differences in the example above highly depends > on the work done in the loop body anyway, so whining about possible > performance issues is quite a bit premature here. ;) Maybe, but you started talking about performance. ;-)

Nono, you did: "Everything that obscures what's going on under the hood has the potential to generate performance problems."

IMO, almost every language construct of a modern language (modern as in "invented about 30 years ago") has the potential to just do that. Loops may hide costly branches, string concatenations may hide costly memory copy operations (not even talking about AnsiStrings here, where an additional overhead of memory allocation/deallocation may occur), costly lookups and indirect calls are hidden by virtual methods that may look like simple procedure calls, or the worst: operator overloading enables the programmer to hide practically anything inside a simple "+" (nothing stops you from doing "c = a + b" where "a" and "b" are pulled from an internet daabase and "c" is stored back there, all hidden in a simple addition), etc. pp.

Even in C (which is a language widely accepted as one where nothing is hidden from the programmer[1]) you can assign structs to each other which - depending on the size and layout of the structure - may result in rather costly memory copy operations.

So to get to the point, finally: The argument that some particular language construct has the potential to generate performance problems is NIL. IMNSHO.

Vinzent.

[0] By removing the need for labels.
[1] Nothing but bugs, hehe. :-> SCNR.

leledumbo
to Vinzent Höfler
Show quoted text 5 lines
Let me take a rather extreme point of view: After all, all those for-, while-, and repeat-until-loops are only there to save you from some typing work[0], because Pascal already has a perfectly good loop-construct with which you can do all that: goto. Would you agree here? Probably not.

I agree with this. After all, every high level constructs provided is meant to help programmers from typing operations which are complicated (or not so easy to write) with simple ones.

View this message in context: http://www.nabble.com/for-..-in-loop-implementation-tp21327686p21347860.html Sent from the Free Pascal - General mailing list .
Florian Klaempfl
to Vinzent Höfler: I won't judge on the "only save some writing" here, but generally it *does* apply to…
Show quoted text 18 lines
Jürgen Hestermann wrote: >> Mantra: First make it work, then make it fast. > > In general that's true from the programmer's viewpoint. But this does > not apply to adding language details because there is no 'first make > it work'. Why obscure important implementation details if the only > benefit is saving some writing? I won't judge on the "only save some writing" here, but generally it *does* apply to language features: If a particular language feature helps you writing correct code faster (by supporting you to make it work instead of relying on your abilities to use the debugger), then it does apply. The real question is if the (or any other) proposal is "good enough" to do just that, or if it's really some syntactic sweetener (not even sugar, sugar at least contains energy). That's something to discuss.

There is not much to discuss: Who has time to implement it without hurting the implementation of other maybe more important stuff. If someone provides usable (readable, commented code with tests, full implementation support also the iterating interfaces) patches to implement the for in ... stuff I'am sure they get applied.

Show quoted text 6 lines
Let me take a rather extreme point of view: After all, all those for-, while-, and repeat-until-loops are only there to save you from some typing work[0], because Pascal already has a perfectly good loop-construct with which you can do all that: goto. Would you agree here? Probably not.

Not really because the use case of typical while/for/repeat loops is much wider. How often do you iterate over the odd days of a week?

Martin Friebe
to Vinzent Höfler: I won't judge on the "only save some writing" here, but generally it *does* apply to…
Vinzent Höfler wrote 23 lines
Jürgen Hestermann wrote: >> Mantra: First make it work, then make it fast. > In general that's true from the programmer's viewpoint. But this does > not apply to adding language details because there is no 'first make > it work'. Why obscure important implementation details if the only > benefit is saving some writing? I won't judge on the "only save some writing" here, but generally it *does* apply to language features: If a particular language feature helps you writing correct code faster (by supporting you to make it work instead of relying on your abilities to use the debugger), then it does apply. The real question is if the (or any other) proposal is "good enough" to do just that, or if it's really some syntactic sweetener (not even sugar, sugar at least contains energy). That's something to discuss. Let me take a rather extreme point of view: After all, all those for-, while-, and repeat-until-loops are only there to save you from some typing work[0], because Pascal already has a perfectly good loop-construct with which you can do all that: goto. Would you agree here? Probably not.

1) "goto" alone (labels assumed) can not replace any of the loop constructs. It must be paired with "if" (or anything to check conditions)

2) Otherwise you are of course right. Following this logic however most programming languages (including pascal and assembler) become redundant. They have far to many instructions to archive the same thing. Look at languages like "whitespace" or "brainfuck" and you will see that a language which is kept simple (by the meaning of "no unneeded instructions" does not need more than 6 to 8 instructions.

SCNR.

Jeremy Cowgar
to Marco van de Voort: In our previous episode, leledumbo said: Your assesment is correct as far as I can see.…
Marco van de Voort wrote 15 lines
In our previous episode, leledumbo said: > The latter one has iteration overheads, while the former can be optimized to > loop as many as needed. I'm not saying I'm the best Pascal programmer, but > in case there's a (better) solution to this (rather than extending the > language) please tell me. > Your assesment is correct as far as I can see. However IMHO that doesn't meant you are right. One single case of an optimalization advantage, and then in a border case like iteration over a constant set does not justify a language extension.

What is the negative of adding it? To me, I program in a language because it makes my life easy, not because each keyword has a certain/clear purpose and cannot be done any other method. The easier FP is for me to program in (or the less typing I have to do), the better FP is to me. I'm confused on why not to add it.

Jeremy

Marco van de Voort
to Jeremy Cowgar

In our previous episode, Jeremy Cowgar said:

Show quoted text 5 lines
> However IMHO that doesn't meant you are right. One single case of an > optimalization advantage, and then in a border case like iteration over a > constant set does not justify a language extension. What is the negative of adding it?

Clutter, maintenance, and somebody has to do both implementation and maintenance, addition to documentation tool, pretty printer etc.

Show quoted text 5 lines
To me, I program in a language because it makes my life easy, not because each keyword has a certain/clear purpose and cannot be done any other method. The easier FP is for me to program in (or the less typing I have to do), the better FP is to me. I'm confused on why not to add it.

"Easy" and typing are not related. Easy allows me to do my job quickly. Typing has never been limiting in my job.

Jeremy Cowgar
to Marco van de Voort
Marco van de Voort wrote 8 lines
In our previous episode, Jeremy Cowgar said: > What is the negative of adding it? > Clutter, maintenance, and somebody has to do both implementation and maintenance, addition to documentation tool, pretty printer etc.

I wonder how much maintenance would actually have to be done? Once a language construct is present, does it really take that much to keep it up? When is the last time the While loop was bug fixed? The same for the additions to documentation, pretty printer, etc... It's all done once and should be fine after that.

However, I am speaking out of a not ever looking at the compiler source, so I may be pretty far off base. Just thinking aloud.

Jeremy

Marco van de Voort
to Jeremy Cowgar

In our previous episode, Jeremy Cowgar said:

Show quoted text 7 lines
> Clutter, maintenance, and somebody has to do both implementation and > maintenance, addition to documentation tool, pretty printer etc. > I wonder how much maintenance would actually have to be done? Once a language construct is present, does it really take that much to keep it up?

Well, I only see the practice from years of reading bugreports, but :

often bugreports pertaining to language features are about special cases and interactions between features. Like automated types, properties etc. What if the right side is an expression instead of a value or a constant? What if it involves ref counted types, what if it involves a temp location ? Etc.

The same for the additions to documentation, pretty printer, etc... It's all done once and should be fine after that.

After a few additions, patches and tweaks, usually yes. And then it turns up that Delphi also accepts <xxxx> after "in" etc.

However, I am speaking out of a not ever looking at the compiler source, so I may be pretty far off base. Just thinking aloud.

People typically only think of a few standard cases like there are in the manual. It is a totally different magnitude to fix any constuction the users find in their existing Delphi code.

If you think it is worth it, by all means go ahead.

Jürgen Hestermann
to Vinzent Höfler: I won't judge on the "only save some writing" here, but generally it *does* apply to…
Show quoted text 6 lines
Vinzent Höfler schrieb: I won't judge on the "only save some writing" here, but generally it *does* apply to language features: If a particular language feature helps you writing correct code faster (by supporting you to make it work instead of relying on your abilities to use the debugger), then it does apply.

What I fear (from observing the last decade or two) is that the language becomes bloated by adding one gimmick after the other just to say "me too". Every new feature is something that a (new) user has to learn about. And the more types of FOR loops you add the more confusing it gets.

Show quoted text 5 lines
Let me take a rather extreme point of view: After all, all those for-, while-, and repeat-until-loops are only there to save you from some typing work[0], because Pascal already has a perfectly good loop-construct with which you can do all that: goto. Would you agree here? Probably not.

Of course not. But would you on the other side agree with adding each and every gimmick mankind can think of? Probably not either.

> Maybe, but you started talking about performance. ;-) Nono, you did: "Everything that obscures what's going on under the hood has the potential to generate performance problems."

You are right that not you started talking about performance but "leledumbo" in his starting post about this topic where he assumed a performance gain:

"The latter one has iteration overheads, while the former can be optimized to loop as many as needed."

So none of us started talking about it. ;-)

So to get to the point, finally: The argument that some particular language construct has the potential to generate performance problems is NIL. IMNSHO.

I doubt that. I often stumble over programs which are awfully slow (Novell Salvage Dialog for examples sometines needs many minutes to sort 80 "objects"!). And that compared with the everywhere heard statement "You don't need to think about performance, it's only important that it runs at all" makes me thinking. Many programmers simply don't know about how their mighty language functions perform and what they do in the background. But without knowing this you don't even have an idea of how fast it will be.

I also had such experience with Virtual Pascal where the FindFirst/FindNext API functions are obscured by an extra layer to support multiple platforms (which I didn't know at first). I only wondered why my simple scan program for the creation date of files took 2 days on our server. After removing all the overhead by directly using FindFirst/FindNext from the windows API it boosted up to 6 hours only. Sure, many people don't care whether a program runs 2 days or 6 hours but I do.

Jürgen Hestermann.

Jonas Maebe
to Jürgen Hestermann
Jürgen Hestermann wrote 17 lines
I doubt that. I often stumble over programs which are awfully slow (Novell Salvage Dialog for examples sometines needs many minutes to sort 80 "objects"!). And that compared with the everywhere heard statement "You don't need to think about performance, it's only important that it runs at all" makes me thinking. Many programmers simply don't know about how their mighty language functions perform and what they do in the background. But without knowing this you don't even have an idea of how fast it will be. I also had such experience with Virtual Pascal where the FindFirst/ FindNext API functions are obscured by an extra layer to support multiple platforms (which I didn't know at first). I only wondered why my simple scan program for the creation date of files took 2 days on our server. After removing all the overhead by directly using FindFirst/FindNext from the windows API it boosted up to 6 hours only. Sure, many people don't care whether a program runs 2 days or 6 hours but I do.

These are examples of bad underlying implementations. In both of these cases the implementation can probably be optimised to deliver close to maximal performance. The evolution to more and more abstraction is simply because there are more and more different underlying platforms, and because the days that most programmers were able to take full advantage of the underlying platform are long gone. With the moves to system and process virtual machines, many-cores, cloud computing, accelerators etc. this is only getting worse.

Not to mention that going as low level as possible to get the last bit of performance will mean that you'll have to redo that work over and over again, unless you stay with one particular generation of one particular platform.

Jonas

Lee Jenkins
to leledumbo: OK, I've read http://wiki.freepascal.org/Modernised_Pascal this (and the FAQ as well).…
leledumbo wrote 6 lines
OK, I've read http://wiki.freepascal.org/Modernised_Pascal this (and the FAQ as well). I disagree with any statement saying that for .. in loop is only a type-saver. It's a good language extension and should be included (since Delphi already have this, it will also be a good idea). Consider the following example:

I'm not sure when Delphi first implemented support For..In but I've been using D2007 and now D2009 without ever using the construct once. That's not to say it would be useful to use, I have just never really needed it I guess.

Warm Regards, Lee