5

I've got a pretty simple piece of arithmetic, but for readability and correctness, I've expanded it out into a bunch of temporary variables. It's easier to read, change, and examine partial pieces of the expression.

My question is, will this code take a run-time beating just because it's expanded? I don't know how the perl compiler works, but in 'C', or such, these extra (not quite) variables would be optimized right out of existence.

my $targetBasis = $$s{"basis"} * $nextBasisPct;
my $dollarsHeld = $$s{"basis"} * $$s{"held"};
my $targetDollars = $dollarsHeld + $opt{"buyDollars"};
my $targetShares = $targetDollars / $targetBasis;
my $newShares = $targetShares - $$s{"held"};
my $targetPrice = $opt{"buyDollars"} / $newShares;

Thanks.

-E

I just got done expanding this from a nasty looking one liner, only to find that it was correct. I'd rather not put it back to unreadable if there's no reason to do so.

Erik Bennett
  • 1,049
  • 6
  • 15

3 Answers3

9

No.

The following The compiled version of the program

$ perl -MO=Concise,-exec a.pl
1  <0> enter
2  <;> nextstate(main 2 a.pl:2) v:*,&,{,x*,x&,x$,$
3  <0> padrange[$s:2,8; %opt:2,8; $targetBasis:2,8] vM/LVINTRO,range=3
4  <;> nextstate(main 3 a.pl:3) v:*,&,{,x*,x&,x$,$
5  <+> multideref($s->{"basis"}) sK/STRICT
6  <+> multideref($s->{"held"}) sK/STRICT
7  <2> multiply[t5] sK/2
8  <0> padsv[$dollarsHeld:3,8] sRM*/LVINTRO
9  <2> sassign vKS/2
a  <;> nextstate(main 4 a.pl:4) v:*,&,{,x*,x&,x$,$
b  <0> padsv[$dollarsHeld:3,8] s
c  <+> multideref($opt{"buyDollars"}) sK
d  <2> add[t7] sK/2
e  <0> padsv[$targetDollars:4,8] sRM*/LVINTRO
f  <2> sassign vKS/2
g  <;> nextstate(main 5 a.pl:5) v:*,&,{,x*,x&,x$,$
h  <0> padsv[$targetDollars:4,8] s
i  <0> padsv[$targetBasis:2,8] s
j  <2> divide[t9] sK/2
k  <0> padsv[$targetShares:5,8] sRM*/LVINTRO
l  <2> sassign vKS/2
m  <;> nextstate(main 6 a.pl:6) v:*,&,{,x*,x&,x$,$
n  <0> padsv[$targetShares:5,8] s
o  <+> multideref($s->{"held"}) sK/STRICT
p  <2> subtract[t11] sK/2
q  <0> padsv[$newShares:6,8] sRM*/LVINTRO
r  <2> sassign vKS/2
s  <;> nextstate(main 7 a.pl:7) v:*,&,{,x*,x&,x$,$
t  <+> multideref($opt{"buyDollars"}) sK
u  <0> padsv[$newShares:6,8] s
v  <2> divide[t13] sK/2
w  <0> padsv[$targetPrice:7,8] sRM*/LVINTRO
x  <2> sassign vKS/2
y  <@> leave[1 ref] vKP/REFC
a.pl syntax OK

Note that I prepended the following to the program first:

use strict;
my ($s, %opt, $targetBasis);

Optimizing the variables away would save copying a value into them, but copying a numerical scalar into another one is very cheap.

I would leave the code in its readable form. Readability is far more important than saving a few nanoseconds.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Man, you're batting 1.000 this week. Thanks! I agree about the readability. I think I was going to leave it anyway in the event that I want to inspect the sub-expressions for other reasons. It is awesome to learn how to get the output to investigate this. Someday I'll learn how to read it. – Erik Bennett Feb 08 '19 at 22:09
  • This is what I started with. `my $targetPrice = $opt{"buyDollars"}/(((($$s{"basis"}*$$s{"held"})+$opt{"buyDollars"})/$targetBasis)-$$s{"held"});`. I should have put that in the original posting. – Erik Bennett Feb 08 '19 at 22:13
  • 2
    `padrange`/`padsv` are lexical (`my`) variable lookups. `LVINTRO` means it's the `my` itself. `multideref` is a new operator that subsumes other operators including lexical variable lookups) for efficiency. – ikegami Feb 08 '19 at 22:58
5

In general, Perl does not ever optimize out variables, only constant expressions, because these are known during the parsing and compilation phase, whereas variable assignment happens at runtime. Consider that any of those variables could be used again later in the scope, or a reference to them could be taken by something and used elsewhere. The Perl parser works sequentially so that would be really difficult to account for.

As for your concerns, unless you are writing code with a real-time requirement (why would you use Perl for this?) or running this code in a loop millions of times, the overhead of variable declaration is not something to be concerned about. Sub calls are slightly more noticeable, method calls moreso, but inefficient algorithms dwarf those concerns. When in doubt, benchmark.

Grinnz
  • 9,093
  • 11
  • 18
  • 1
    Good information about the sequential nature of the parser. Thanks. As to 'Why would you use perl for this?', I'd say, it's a very expressive language. If I need fast execution, I use 'C'. If I need fast development time, I use perl. If I need really good type checking, I use Java or C++. I am running though millions of lines, but not so often that I need to sweat it. Someone clever, whose name escapes me, echoed your notion of inefficient algorithms. "There is not now, nor will there ever be a language in which it is the least bit difficult to write a bad program." – Erik Bennett Feb 09 '19 at 19:04
  • 1
    @ErikBennett I wasn't asking why you use Perl for your task, but why you (general you) would use it for real-time sensitive applications, which is a very specific and difficult requirement. – Grinnz Feb 09 '19 at 19:57
  • I understand. It's not real time. – Erik Bennett Feb 09 '19 at 20:14
5

You already have a couple of good answers to your question, but I wanted to add a couple of tips for improving the readability of your code.

  • Hash keys don't need to be quoted unless they contain whitespace. So $opt{"buyDollars"}, for example, can be written as $opt{buyDollars}.
  • It looks like you have a hash reference in $s. The standard way to access a value from a hash reference is $s->{key}. You're using $$s{key} which works, but which will be very confusing to most people reading your code.
Dave Cross
  • 68,119
  • 3
  • 51
  • 97
  • Both good points, and both reveal to some degree my age. The `$opt{"buyDollars"}` notation is to make sure that I'm not getting some unintended side effect of using a bareword, like a subref, or something. It happened once to me and "made a mess". The `$$s{key}` notation predates the recent introduction of the `$hash->{}` notation. "Recent" being a relative term. – Erik Bennett Feb 09 '19 at 18:51
  • 1
    @ErikBennett I certainly did the same for a while with quoting hash keys. But with a good understanding of how hash key quoting works, you can be certain that it will not happen. As long as it is a valid identifier, consisting only of letters, digits, and underscore, and not starting with a digit; then it will always be treated as a string. – Grinnz Feb 09 '19 at 20:00
  • I don't remember the specifics, and it may no longer be the case, but at one point I did something like `$foo{delete}` or `$foo{sort}` and it didn't do what I wanted. This may have been perl3 or perl4. It was a while ago. I'll read up on the current state of affairs. Thanks. – Erik Bennett Feb 09 '19 at 20:12
  • `$h->{...}` is not recent by any measure. It's been with us since at least 5.6, which came out almost 20 years ago. Most of CPAN only offers compatibility to 5.8 – ikegami Feb 09 '19 at 21:35
  • Yes, that's why I said earlier, "recent" is a relative term. Perl 5 was quite an expansion on Perl 4. – Erik Bennett Feb 11 '19 at 18:26