Even _if_ passing the pointer to a struct is a tiny bit slower, I much prefer to just collect all the needed information into one struct and pass a reference to it, than having to pass 12 variables into a function.R. Tomasi wrote: ↑Mon Sep 20, 2021 11:03 pm Well, yes and no. Of course the size of a pointer is typically 8 or 4 bytes, depending on your hardware. But passing arguments to a function does usually not involve copying memory, since the first n arguments will be passed in registers. The exact value of n and which registers are used depend on the calling convention. Only when too many arguments are needed they will be passed via the stack. And I guess that's the catch: the registers used for passing parameters are in use and the compiler can't use them for local variables when it is optimizing the function. Most calling conventions are designed to balance between using registers for parameters and for local variables. But if you have less parameters, there is more room for the compiler to optimize. That last sentence is a hunch of mine - I'm not exactly sure how big the impact is. That's why I am asking if someone has experimented with that. The drawback of passing a pointer/reference to a struct, is that this struct will most certainly live on the stack, which means memory accesses will be involved and those may be slow. I would however suspect that the local stack is cached.
Simplifying code II
Moderators: hgm, Rebel, chrisw
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: Simplifying code II
-
- Posts: 307
- Joined: Wed Sep 01, 2021 4:08 pm
- Location: Germany
- Full name: Roland Tomasi
Re: Simplifying code II
I am very much with you on that. Which is slower/faster really depends on many factors: how many arguments? what calling convention? are there so many local variables in the function that register load is even an issue? How big is the struct? (if it fits in a register passing it by value might be faster), etc. So leaving the maintainability arguments aside (which are indeed strong arguments), you will really have to measure what works best in each individual case.mvanthoor wrote: ↑Mon Sep 20, 2021 11:13 pmEven _if_ passing the pointer to a struct is a tiny bit slower, I much prefer to just collect all the needed information into one struct and pass a reference to it, than having to pass 12 variables into a function.R. Tomasi wrote: ↑Mon Sep 20, 2021 11:03 pm Well, yes and no. Of course the size of a pointer is typically 8 or 4 bytes, depending on your hardware. But passing arguments to a function does usually not involve copying memory, since the first n arguments will be passed in registers. The exact value of n and which registers are used depend on the calling convention. Only when too many arguments are needed they will be passed via the stack. And I guess that's the catch: the registers used for passing parameters are in use and the compiler can't use them for local variables when it is optimizing the function. Most calling conventions are designed to balance between using registers for parameters and for local variables. But if you have less parameters, there is more room for the compiler to optimize. That last sentence is a hunch of mine - I'm not exactly sure how big the impact is. That's why I am asking if someone has experimented with that. The drawback of passing a pointer/reference to a struct, is that this struct will most certainly live on the stack, which means memory accesses will be involved and those may be slow. I would however suspect that the local stack is cached.
-
- Posts: 307
- Joined: Wed Sep 01, 2021 4:08 pm
- Location: Germany
- Full name: Roland Tomasi
Re: Simplifying code II
I consider that a good practice when writing classes, too. Generally I somewhat try to adhere to hungarian notation (https://en.wikipedia.org/wiki/Hungarian_notation), but I am not pedantic about it, either - class members being the only exception: they always get prefixed by m_.pedrojdm2021 wrote: ↑Mon Sep 20, 2021 9:42 pm you can also use "m_variableName" for local variables inside your class
-
- Posts: 307
- Joined: Wed Sep 01, 2021 4:08 pm
- Location: Germany
- Full name: Roland Tomasi
Re: Simplifying code II
I tried passing the search arguments in a struct in Pygmalion, to see if it pays off. To my big surprise it seems to hurt performance by roughly 10%.
This is the version with individual arguments:
This is the version where the arguments are passed through a struct:
I had to pass the struct as rvalue reference and not as lvalue reference, since the compiler wouldn't allow me to instantiate the struct directly in the function call, elsewise. I'm not sure what exactly the compiler is doing under the hood, but I find these results rather discouraging. I will change the code to use lvalue references, just to be sure that isn't the reason, but I'm not holding my breath.
This is the version with individual arguments:
Code: Select all
0: 0 -
1.00 N in 32.9 mcs => 30.4 kN/s
1: +0.0527344 - e3
24.0 N in 61.6 mcs => 390 kN/s
2: 0 - e3 e6
78.0 N in 85.7 mcs => 910 kN/s
3: +0.03125 - e3 d5 Qh5
565 N in 401 mcs => 1.41 MN/s
4: 0 - Nc3 d5 d4 Nc6
2.90 kN in 1.97 ms => 1.47 MN/s
5: +0.0214844 - Nc3 d5 d4 Nc6 f3
7.26 kN in 4.28 ms => 1.70 MN/s
6: 0 - Nc3 d5 d4 Nc6 Nf3 Nf6
56.1 kN in 35.2 ms => 1.59 MN/s
7: +0.0195312 - Nc3 d5 d4 Nc6 Nf3 Nf6 Qd3
185 kN in 130 ms => 1.42 MN/s
8: 0 - Nc3 d5 d4 Nc6 Nf3 Nf6 Qd3 Qd6
1.57 MN in 986 ms => 1.59 MN/s
9: +0.0117188 - Nc3 d5 d4 Nc6 f3 Nf6 Qd3 e5 xe5 Nxe5
4.57 MN in 3.05 s => 1.50 MN/s
10: +0.00195312 - Nc3 Nc6 e4 e5 Nf3 Nf6 d4 xd4 Nxd4 Bd6
57.2 MN in 38.6 s => 1.48 MN/s
Code: Select all
0: 0 -
1.00 N in 38.7 mcs => 25.8 kN/s
1: +0.0527344 - e3
24.0 N in 66.8 mcs => 359 kN/s
2: 0 - e3 e6
78.0 N in 128 mcs => 607 kN/s
3: +0.03125 - e3 d5 Qh5
565 N in 511 mcs => 1.11 MN/s
4: 0 - Nc3 d5 d4 Nc6
2.90 kN in 2.13 ms => 1.36 MN/s
5: +0.0214844 - Nc3 d5 d4 Nc6 f3
7.26 kN in 5.47 ms => 1.33 MN/s
6: 0 - Nc3 d5 d4 Nc6 Nf3 Nf6
56.1 kN in 38.2 ms => 1.47 MN/s
7: +0.0195312 - Nc3 d5 d4 Nc6 Nf3 Nf6 Qd3
185 kN in 128 ms => 1.45 MN/s
8: 0 - Nc3 d5 d4 Nc6 Nf3 Nf6 Qd3 Qd6
1.57 MN in 1.10 s => 1.43 MN/s
9: +0.0117188 - Nc3 d5 d4 Nc6 f3 Nf6 Qd3 e5 xe5 Nxe5
4.57 MN in 3.27 s => 1.40 MN/s
10: +0.00195312 - Nc3 Nc6 e4 e5 Nf3 Nf6 d4 xd4 Nxd4 Bd6
57.2 MN in 41.2 s => 1.39 MN/s
-
- Posts: 307
- Joined: Wed Sep 01, 2021 4:08 pm
- Location: Germany
- Full name: Roland Tomasi
Re: Simplifying code II
Here are the results for lvalue references:
That seems to be equivalent, if not a tiny tad worse...
I'm all for readability and maintainability... but honestly, 10% is too much of a price to pay. I will stick with individual arguments.
Code: Select all
0: 0 -
1.00 N in 35.2 mcs => 28.4 kN/s
1: +0.0527344 - e3
24.0 N in 66.1 mcs => 363 kN/s
2: 0 - e3 e6
78.0 N in 130 mcs => 601 kN/s
3: +0.03125 - e3 d5 Qh5
565 N in 400 mcs => 1.41 MN/s
4: 0 - Nc3 d5 d4 Nc6
2.90 kN in 2.12 ms => 1.37 MN/s
5: +0.0214844 - Nc3 d5 d4 Nc6 f3
7.26 kN in 6.01 ms => 1.21 MN/s
6: 0 - Nc3 d5 d4 Nc6 Nf3 Nf6
56.1 kN in 42.6 ms => 1.32 MN/s
7: +0.0195312 - Nc3 d5 d4 Nc6 Nf3 Nf6 Qd3
185 kN in 137 ms => 1.35 MN/s
8: 0 - Nc3 d5 d4 Nc6 Nf3 Nf6 Qd3 Qd6
1.57 MN in 1.11 s => 1.41 MN/s
9: +0.0117188 - Nc3 d5 d4 Nc6 f3 Nf6 Qd3 e5 xe5 Nxe5
4.57 MN in 3.24 s => 1.41 MN/s
10: +0.00195312 - Nc3 Nc6 e4 e5 Nf3 Nf6 d4 xd4 Nxd4 Bd6
57.2 MN in 41.3 s => 1.39 MN/s
I'm all for readability and maintainability... but honestly, 10% is too much of a price to pay. I will stick with individual arguments.
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code II
Method should contain no more than one return statement.
Git is my only friend.
O wait it is control z (undo in visual studio)
Git is my only friend.
O wait it is control z (undo in visual studio)
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: Simplifying code II
That, IMHO, is an old view.
If you are running through a function (alpha/beta being one of them) which can hit a point where you MUST return (time up, for example), it's much cleaner and more understandable to just return from that point, as opposed to saving the return value somewhere else, finishing the function without actually doing anything, and then return at the end.
When you have code like "if stuff_happened() { return 0 }", then others just know: "If this happens, we're done", and you don't need to take "stuff_happens()" into account anymore. If you save "true" in a temporary variable, it will haunt you the rest of the function, and you'll have to check on it every time the function needs to do something.
I had a teacher in university (beginning of 2000's) who was quite old at that time already, and he held this view. He'd actually deduct points for each extra return statement. Sometimes our code got VERY messy, trying to keep track of all the temp variables: "if x == 5 && !a && !b && !c {...}"
When you hit a point where you need to return, then just be done with it and return. It will greatly unmessify your code.
-
- Posts: 307
- Joined: Wed Sep 01, 2021 4:08 pm
- Location: Germany
- Full name: Roland Tomasi
Re: Simplifying code II
I totally agree. That's an outdated practice imho, too. There might have been performance reasons for doing so back in the days, but I hardly believe that is an issue at all with modern compilers. And checking flags abundantly doesn't help with performance, either, I would argue. There was a limitation of that kind for constexpr functions in older C++ standards, but that's gone, too.mvanthoor wrote: ↑Wed Sep 22, 2021 6:26 pmThat, IMHO, is an old view.
If you are running through a function (alpha/beta being one of them) which can hit a point where you MUST return (time up, for example), it's much cleaner and more understandable to just return from that point, as opposed to saving the return value somewhere else, finishing the function without actually doing anything, and then return at the end.
When you have code like "if stuff_happened() { return 0 }", then others just know: "If this happens, we're done", and you don't need to take "stuff_happens()" into account anymore. If you save "true" in a temporary variable, it will haunt you the rest of the function, and you'll have to check on it every time the function needs to do something.
I had a teacher in university (beginning of 2000's) who was quite old at that time already, and he held this view. He'd actually deduct points for each extra return statement. Sometimes our code got VERY messy, trying to keep track of all the temp variables: "if x == 5 && !a && !b && !c {...}"
When you hit a point where you need to return, then just be done with it and return. It will greatly unmessify your code.
EDIT: I just googled what the original reason for this was. Seems like "one return statement" never was actually meant. It was all about "one return adress" - which is quite a different thing:
https://softwareengineering.stackexchan ... -come-from
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code II
Maybe not use 'else'. Makes refactoring easier.
Nested if statements terrible to read.
Nested if statements terrible to read.
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code II
I have a method DoSearchMoves with 14 parameters. Awfull. Don't know what to do about it.
Put some in an extra object?
Put some in an extra object?