Recent

Author Topic: How optimized is the FPC compiler  (Read 13596 times)

Shpend

  • Full Member
  • ***
  • Posts: 150
Re: How optimized is the FPC compiler
« Reply #105 on: December 26, 2020, 11:06:45 am »
Quote
Believe what you want. I know the compiler's code, you don't.
Well, this is true but as I stated multiple times, there is nothing wrong considering addition to the compiler which would make sense, obviously you have to check for yourself how hard it would be to add them but its far away from being an "inappropiate/unlogical Addition".
This aside for a moment, but atleast the more important stuff are the mangement operators, this is more a must have for completion than typehelpers, since they offer way more opputornities than type-helper.

beepee

  • New member
  • *
  • Posts: 6
Re: How optimized is the FPC compiler
« Reply #106 on: December 26, 2020, 04:40:06 pm »
Hi,
 I generally notice the executables compiled by Free Pascal are over twice as big and run about half the speed compared with the same code compiled by Delphi7.
Except for my graphic programs, there the Free Pascal executable is about same speed and has less bugs than Delphi7
(my delphi7 is from ~2005, so quite old and 64 bits things are not well supported (e.g. seek() )in this version)

Edit:
I apologize, using the fpc320 version, I see the FreePascal is slightly faster, but code size still bigger. And to @Shpend below: yes, all optimizations I can find are used, no debug

To @Handoko below: I will take a look at Build Modes, but I am used to compile from the command line with fpc.exe that uses an fpc.cfg where debug info is off and optimize and strip are set already. And OK, somewhat bigger code is not a big problem nowadays.
« Last Edit: December 26, 2020, 06:24:37 pm by beepee »

Shpend

  • Full Member
  • ***
  • Posts: 150
Re: How optimized is the FPC compiler
« Reply #107 on: December 26, 2020, 05:35:38 pm »
@beepee

did u look if u were compiling with Debug? and were the platform target the same?

Handoko

  • Hero Member
  • *****
  • Posts: 4345
  • My goal: build my own game engine using Lazarus
Re: How optimized is the FPC compiler
« Reply #108 on: December 26, 2020, 05:42:19 pm »
Lazarus / Free Pascal generate bigger binaries because it compiles to multiple platforms, so some extra codes need to be added. Also it is not fair to compare with Delphi 7 because it supports new features, I'm not very sure maybe unicode, etc.

Because the compiler supports several optimization techniques.  The size difference won't be noticeable on large projects.

But, maybe you haven't know. The default configuration will add the debugger info to the generated binary. If you use Lazarus you can disable it by:
Lazarus main menu > Project > Project options > on the left side > Debugging > disable Generating Debugging Info and enable Strip Symbols

There are some other extra things to make sure you get the smallest binary, you can search the forum to know more.

To make it easier to enable/disable the configuration settings, you should enable and use Build Modes. You can check the documentation if you're interested.
« Last Edit: December 26, 2020, 05:54:06 pm by Handoko »

Warfley

  • Hero Member
  • *****
  • Posts: 549
Re: How optimized is the FPC compiler
« Reply #109 on: December 26, 2020, 06:04:07 pm »
the thing only is it could be really complete the niche of old-style-objects, when it would have :
I would not say this, because C++ has a lot of differences that not necessarily are in the memory model itself, but allow for a lot of things in C++ that makes C++ much more convinient for high performance programming.

For example in C++ there are references, which are semantically like a one time pointer (i.e. a pointer that once being set cannot be changed, and is not allowed to be null/nil) but syntactically behave like a direct access to the object:
Code: C  [Select][+][-]
  1.   int i_local;
  2.     int &i_ref = i_local;
  3.     i_ref = 42;
  4.     std::cout << i_local << std::endl;
And there is a lot of shanannigance you can do with templates, vor example variable template arguments. This for example allows for some very neat stuff like the creation of objects inplace in other datastructures.

Let me give you an example. Let's say you want to construct a large graph. In this situation, you allocate a lot of memory in a short amount of time (during construction of the graph), and deallocate all of that at once (when tearing the whole graph down).
Using the heap directly might here be too slow, as heap allocations and freeing of every node takes time, you want to allocate the memory in bulks and free it all at once. Also in a classical approach, to free the nodes, you need to traverse the tree, which is terrible for cache locality.
In this case you want to use a stack allocator. This is a datatstructure that reserves a large amount of memory and allocates objects on it like a stack. You can't free objects during the runtime, but once the stack is teared down, all objects are going to be simultaniously destroyed.
This is how you would build that in C++:
Code: C  [Select][+][-]
  1. struct TreeNode {
  2.   virtual int child_count() = 0; // abstract method
  3.   virtual TreeNode &get_child(int idx) = 0; // abstract method
  4.   virtual int get_value() = 0; // abstract method
  5. }
  6. struct TreeBranch: public TreeNode {
  7.   TreeNode &left;
  8.   TreeNode &right;
  9.   virtual int child_count() override { return 2; }
  10.   virtual TreeNode &get_child(int idx) override { return idx ? left : right; }
  11.   virtual int get_value() override { return left.get_value() + right.get_value(); }
  12.   TreeBranch(TreeNode &_left, TreeNode &_right): left(_left), right(_right) { } // constructor
  13. }
  14. struct TreeLeaf: public TreeNode {
  15.   int value;
  16.   virtual int child_count() override { return 0; }
  17.   virtual TreeNode &get_child(int idx) override { assert(false); }
  18.   virtual int get_value() override { return value; }
  19.   TreeLeaf(int _value): value(_value) { }
  20. }
  21. ...
  22. std::vector<TreeBranch> branch_memory{1024*1024*1024}; // capacity 1 GB (virtual memory)
  23. std::vector<TreeLeaf> leaf_memory{1024*1024*1024}; // capacity 1 GB (virtual memory)
  24.  
  25. TreeNode &leaf1 = leaf_memory.emplace_back(1);
  26. TreeNode &leaf2 = leaf_memory.emplace_back(2);
  27. TreeNode &leaf3 = leaf_memory.emplace_back(3);
  28. TreeNode &branch1 = branch_memory.emplace_back(leaf1, leaf2);
  29. TreeNode &root = branch_memory.emplace_back(branch1, leaf3);
  30. std::cout << root.get_value();

In this code, not a single copy (or move) operation takes place, all operations are copy by reference. std::vector<T>.emplace constructs a new element directly where it will be stored by passing the arguments given to the function 1-1 to the types constructor.

In pascal the allocator would need to only allocate the memory and return the pointer so the allocating function can manually call the constructor:
Code: Pascal  [Select][+][-]
  1. // let's assume similar definitions of the types
  2. var
  3.   branch_memory: specialize TVector<TTreeBranch>; // let's assume that a type like this exists
  4.   leaf_memory: specialize TVector<TTreeLeaf>;
  5. var
  6.   leaf1, leaf2, leaf3, branch1, root: PTreeNode;
  7. begin
  8.   leaf1 := leaf_memory.emplace_back;
  9.   leaf1^.init(1);
  10.   leaf2 := leaf_memory.emplace_back;
  11.   leaf2^.init(2);
  12.   leaf3 := leaf_memory.emplace_back;
  13.   leaf3^.init(3);
  14.   branch1 := branch_memory.emplace_back;
  15.   branch1^.init(leaf1, leaf2);
  16.   root:= branch_memory.emplace_back;
  17.   root^.init(branch1, leaf2);
  18.   WriteLn(root^.get_value);
  19. end;
You can archive the same behaviour, but it is more code and less readable. So again I am at what I said in my very first post in this thread. You can do the same in pascal as with C++, but in C++ you just write less (and cleaner) code for getting the same efficency. And this is purely due to the language design of C++.

That said, this is still a very niche thing. The example above is a reduced version of a problem I actually had to face, where the overhead of heap allocation was just too slow for my purposes (the graph that was created required multiple gigabytes in memory), so I needed to build a stack allocator.
C++ makes this very easy, because as you can see, std::vector already provides the required functionality. But a lot of programs, especially those for which Pascal is prevalently used (like most GUI programs) do not find themselves in such situations very often.

Pascal is not C++, and does not even try to be like C++. Different languages have different strengths, and honestly, C++ is much more complex than Pascal, and while it is great for such things as shown above, I would never use C++ for just building a small GUI application due to it's complexity. C++ is also much harder to learn than Pascal due to exactly this.
Different languages are like different tools. I don't need Pascal to be like C++, the same way I don't need to add a hammer head to a skrew driver. If you try to do everything, you end up being bad, or at most mediocre, at everything.

To summarize, Pascal and C++ have by their design different goals. I would argue thats a good thing. I like management operators not because they allow for efficient programming, but because they clean up the code. C++ has the intendet goal to be as efficient as possible, all language features are designed with this in mind, even if that means making the language more complicated. If Pascal would become a C++-lite, there would literally be no reason for me (and probably for most people) to use it instead of C++.
Even though some features would be nice, ultimately we are talking about a niche where Pascal is not the language of choice to begin with. Better to focus on the things Pascal is already good at, instead of trying to improve something Pascal isn't so usefull for to begin with.
« Last Edit: December 26, 2020, 06:08:56 pm by Warfley »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 9586
  • FPC developer.
Re: How optimized is the FPC compiler
« Reply #110 on: December 26, 2020, 09:33:54 pm »
I generally notice the executables compiled by Free Pascal are over twice as big and run about half the speed compared with the same code compiled by Delphi7.

Just like more modern Delphi.  D7 is minimalist and has no deep support for unicode, or anything else after 2003 or so.  If you need to compare to a Delphi, use a recent one, not something ancient.

Also, binary size minimization is no core target at the moment, nobody wants to do complex work on it, like improving smartlinking (except Pascaldragon, occasionally)
 

Shpend

  • Full Member
  • ***
  • Posts: 150
Re: How optimized is the FPC compiler
« Reply #111 on: September 21, 2021, 11:34:54 pm »
Hey, I found this, can some1 explain more for that?

https://gitlab.com/freepascal.org/fpc/source/-/issues/35825

damieiro

  • Full Member
  • ***
  • Posts: 188
Re: How optimized is the FPC compiler
« Reply #112 on: September 23, 2021, 06:42:50 pm »
Well, i have read all the post and i have some contradictory feelings.

First of all, the basis of my argumentation:

- FPC and C, for me, have similar speeds. We can argue if move/copy, using pointers, etc, can be more convenient. But the tools are in Pascal and C. If I really go for speed, i would see (as other saids) how many memory allocs/copys/moves/pointer passing, not data/or system calls i'm being doing. And really needy speed need smart thinking. I do not see many differences between pascal and C in their *basic* mode.
As a very fast example: convert any NTree algorithm in a array algorithm and you would have same speed.

With this point of view, things like a+=b or a:=a+b; for me it's sugar syntax. I goes to the same intermediate code, then to same assembly if properly done. Same if a:=a+1 should give us an inc(a)

But i think we hace several issues related to the class/object model.

1.- We haven't a void heap Class. I haven't read about this in your posts, but Object is not TObject even if both were on heap. Object is a void one. No methods, no data, not any. Object has not an AfterDestructionMethod for example, no interface data and things like that. Object it's A lightweightversion of TObject even on heap. I we need a really void heap class, there is nothing. And i allways said that one of this kind is really needed.
2.- I think we need to determine if we want to be a 25 year compiler that mimics delphi/embarcadero with their good and bad ideas or if besides that, we want a compiler to do their own way and supporting embarcadero/delphi things.
What are the problem?. We cannot use Object for past compatibility (and even embarcadero deprecates it). Well: Use (for example) FPCObject to do new and cooler things: The new and shiny Object implementation for FPC. For use on stack with all the power of a delphi class. Or explore the way. We cannot use other thing that it's not a TObject Descendant. Well, not. We should do a TLazarusClass* (with NOTHING, the minimal one) and then derive TObject. So we can derive from a TLazarusClass* without all the bloated TObject and do newer and cooler and lighter things. And this could be with a cool syntax. Embarcadero wouldn't do it for us. And i think it's clear that we have a mess here. Let's study it and make a good object pascal learning from all our experiences. From the compiler view on how to optimice. From the syntax view to unify things and things like that. We are a community bigger than Embarcadero one and we know the tool.

* (Or other cool name).  :D

3.- I'm a FPC User. I love It. C++ should learn how to do things even with our issues. And i read some posts and seems for their tone that are like we cannot fight and things like that.. Well i think we can fight and do ever better than others, for that reason we are using fpc and lazarus, not the reverse. And i think, perhaps, it's time to think about what we want to be when we were adults, not a child from  oldies doing the same oldies mistakes.

Note: It's not a blame for fpc devs. It's a blame for us, as community to, perhaps, show what we should be the road to do and if, as community, feel confortable with the roadmap. I think many of us doesn't like the oop implementation (it's a desideration, but an open poll will give us a communty view, for example), but there is not a study group from users to do a proposal, nor a poll, not a easy way to make a proposal with a community formal review. And this will enforce a well weighted and balanced evolution with a clear objetive and not our personal tastes (yes i have my own tastes too  :P). Perhaps same with other issues: standard, additional libs, etc..

edit: As example: Figure we are working for a renewed object/class model.
We should:
1.- Do a working group-dev group to do the things. With some basic agreements:
  a) Devs will go for it. If not, there isn't a working group. I would be a theoretically study group with no really trascendence.
  b) Little bites, not big ones. All we have a live.
  c) No *mumble mumble* do your own fork. We are a community, but we aren't too numerous to split or forking and it's a bad strategy. It's better to say: There isn't enough people to do it, but if there were people we will go for it or say: allthough there were enough people for this, we think it's a bad idea for this, and this, and this. And even making a recording/faq/forum to debate these and document that

..Or something like the above if all people doing the work agree with their own terms.
« Last Edit: September 23, 2021, 07:15:45 pm by damieiro »

Shpend

  • Full Member
  • ***
  • Posts: 150
Re: How optimized is the FPC compiler
« Reply #113 on: September 28, 2021, 10:16:19 pm »
I like your view personally, mate!

I hope the dev's really have a watch on that :)

Blade

  • Full Member
  • ***
  • Posts: 142
Re: How optimized is the FPC compiler
« Reply #114 on: September 29, 2021, 12:36:28 am »
But i think we hace several issues related to the class/object model.

Could you give your opinion on advanced records?

Clearly this is direction that Delphi/Embarcadero went, so trying to understand your direction a bit better.

Also, I'm curious if you are coming from a language that was Class-based OOP centric, so feel compelled to continue similar usage in Object Pascal versus the present possible options that it gives.

damieiro

  • Full Member
  • ***
  • Posts: 188
Re: How optimized is the FPC compiler
« Reply #115 on: October 14, 2021, 03:39:26 pm »
Quote
Could you give your opinion on advanced records
Clearly this is direction that Delphi/Embarcadero went, so trying to understand your direction a bit better.

Also, I'm curious if you are coming from a language that was Class-based OOP centric, so feel compelled to continue similar usage in Object Pascal versus the present possible options that it gives.

Advanced records, inmho, is a valid point. It's Rust view also. Does the job if you do not need inheritance in your paradigm. For an oldies view, it's a kind of sugar cake of an old unit (taking a whole unit as a advanced record). It's handy, and their potential as making helpers, generics, etc is a very valuable one. If you need encapsulation but not inheritance, i think it fits perfectly.

I do not blame vs advanced records. An OOP can benefit from them a lot, and, i think, makes a fresh tool for doing things. It makes that not-all that needs encapsulations and can be reused *must be* an object. It was allways Quirckly that, for example, system facilities (like opening/close files), system calls, simply things like a random generator, etc, or there should be a procedural call with assignments for persistent data (assign file, seeds..), or a fully object that rarely could have descendency or hybrids like procedures with const variables.. An advanced records sounds far better for many jobs.
Advantages of an advanced record (INMHO):
- Encapsulation
- Do not use inheritance and saves these space  and system and compiler overhead (no OOP tables, etc) and it's faster.
- Avoids quirks like procedures with constant values not showed (like randomize, random calls). You expect encapsulation of data in an advanced record, but not in a procedure
- Avoids quircks of assignation (like the assign a file variable) and bad style like two-calls for one service.
- Allows modern syntax like generics.. Generics is a very powerful tool.
- Can be used and reused out of the scope of classes, which is handy.
- Better mainteinance.
- Readable and enforces good practices.
- A differect tool that we don't have. And different tools is allways wellcome :)

So, i like advanced records.

On the other hand, i dislike two OOP implementations (one nearly deprecated, and other not empowered full) for the same niche of solutions. I think there is here room for improvements and rethinking.

And for the sake of completness. I'm not coming from a oop centric languaje. As many users, i started c (not c++) and Pascal and assembly (and basic  :D ). Platforms from cp/m-dos/all windows, unixes, solaris, etc. Some prolog, some fortran, but my main base is that. OOP was too modern from me, i adquire the oop base later, but i like it as a very powerful tool for large deployments.
I am firmly convinced on code readability, code security (the languaje should avoid mistakes from programmers), code efficienct and this makes all-purpose. And low level grounded compiler.
Pascal does it. Good read, strong typed, efficient, all purpose. But i think that we are forgetting low-level when we are thinking like : low-level is like a C languaje.
Well. Pascal is low level as C. Safer. Strong typed. Readable. But capable of the same speed and low resource consuption as C. If we voluntary do a TObject implementation with a higher level ground for Nothing, we are giving this ground to C++ for nothing. It's a better aproach a very base TVeryPrimitiveObject (a pure void class), then construct the more advanced TObject we are using, and we will have both advantages. The lower ground that can give us a lot of happines if smart people working in it, and the middle ground level, which is now used.
TP-old-nearlydeprecated-Object were on that lower ground. Not for stack or heaps , but for the base. The Void class as a base in tp-object. We could do a TObject from TPObject, but not the reverse. And this is the key here.

(pd: sorry for the latter answer, i'm having some health issues :( )

and one personal opinion.

If you are from the one-file one-object way of coding... The most beautiful and readable code you can achieve inmho, its FPC code. And really fast compile. And fast result.

« Last Edit: October 14, 2021, 04:15:48 pm by damieiro »

 

TinyPortal © 2005-2018