sportsfan33 has asked for the wisdom of the Perl Monks concerning the following question:

Hi everyone,

I am a hardcore C/C++ programmer and have been using perl for the past few weeks/months.

In our new company, the server side is mostly written in perl. The server side code mostly uses the DBI module to connect to the database, query it, get the results and dispatch those results off to the UI.

Several of the server side modules lie directly in the path to the database. Sometimes, the database queries can fail. Recently, we encountered "mysql deadlocks". Our perl subroutines would just die (or worse yet, could totally ignore the database failures). My task was to catch the database failures and propagate them to the level where the UI would pop up a window for the user.

I decided to go with the try-catch-throw pattern in perl. We are also using Error::Simple (I understand it's deprecated, but nevertheless, that's the practice as of now) to throw and catch the error.

Now, let's say I have a module with several subroutines. Some of the subroutines are "interface functions" that are calling the internal subroutine functions (static function equivalent in C).

Let's say I have one interface function and 2 internal subroutines. Let's say each subroutine can fail somewhere and throws an error. The job of the interface function is to "try" the subroutine, catch the error, and throw the same error, so another interface function that's calling this one can catch it. This chain eventually propagates all the way to the UI.

sub interface_sub { try { internal_sub1($arg1, $arg2); } catch Error with { my $E = shift; throw Error::Simple($E); }; try { internal_sub2($arg1, $arg2, $arg3); } catch Error with { my $E = shift; throw Error::Simple($E); }; }

To reduce the code and make it look better, I decided to call all internal subs using a function reference.

sub exec_internal_sub { my ($sub_ref, @sub_args) = @_; try { $sub_ref->(@sub_args); } catch { my $E = shift; throw Error::Simple($E); }; } sub interface_sub { exec_internal_sub(\&internal_sub1, $arg1, $arg2); exec_internal_sub(\&internal_sub2, $arg1, $arg2, $arg3); }

This code ran fine.

However I got a review from one of the members saying that adding references and dereferences like this can have "serious memory consequences" in perl. He argued that perl is an interpreted language, and all functions are simple strings in memory until we call functions using references. When we add references, now there is a runtime lookup of that function and this can get complicated when the list of arguments is too long.

I did not completely understand what was being talked about here. The concept of a function pointer in C/C++ is simple enough to understand. All functions are accessed by their memory location and passing by pointer adds only 1 lookup operation. Since perl is interpreted, is this somehow different? Are there more penalties I am not aware of?

In my understanding, the perl program is loaded in memory, so calling each function even without a reference should still occur by using the function's address in memory. Passing a reference would just add one more lookup in the operation without any additional cost. I would like to know if I have totally misunderstood perl.

Finally, what's the cleanest way (aesthetically pleasing, not bleeding eyes) to implement try-catch-throw in the situation I have described above?

Replies are listed 'Best First'.
Re: Cost of passing function references
by dave_the_m (Monsignor) on Sep 16, 2015 at 21:01 UTC
    Your reviewer is severely misinformed about how perl works internally. Subs are compiled to an internal form just once, and at run time are accessed via the "CV" slot in a glob structure (f(...) form) or via the "SvRV" slot in a pad variable ($f->(...) form). There is almost no difference between the two.

    I just tried profiling both forms on perl 5.22.0. Calling an empty function with 3 args used 665 x86_64 instruction reads for the direct form, and 675 for the indirect form: a 1.5% difference. For a real sub that actually does something in its body, the difference would be negligible.

    Dave.

Re: Cost of passing function references
by RonW (Parson) on Sep 16, 2015 at 21:31 UTC

    Your reviewer is misinformed.

    Perl code compiles as it is loaded. By the time the first executable line of code in a Perl program is run, the whole program has already been compiled.

    Exceptions:

    Code inside BEGIN blocks is run as soon as the enclosing block has been compiled.

    Code inside modules included by use is run as soon as the module has been compiled. (use is run as soon as it compiled.)

    Code inside modules included by require is not compiled until the require is executed.

    Code inside files included by do is not compiled until the do is executed.

    Code contained in strings is not compiled until evaluated by eval.

    Subroutines loaded by the AUTOLOAD mechanism won't be compiled until your program calls them.

    FYI, there are modules to facilitate autoloading, including AutoLoader and SelfLoader.

    Update: Clarified use and added mention of do.

Re: Cost of passing function references
by runrig (Abbot) on Sep 16, 2015 at 20:57 UTC
    He argued that perl is an interpreted language, and all functions are simple strings in memory until we call functions using references ... I did not completely understand what was being talked about here.
    No worries. The reviewer did not know what he was talking about either. That being said, I like Try::Tiny.
Re: Cost of passing function references
by LanX (Saint) on Sep 17, 2015 at 00:10 UTC
    Could it be that your reviewer is confusing Perl with TCL <8.0 which had no bytecode compiler yet?

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

Re: Cost of passing function references
by Anonymous Monk on Sep 16, 2015 at 18:58 UTC
Re: Cost of passing function references
by locked_user sundialsvc4 (Abbot) on Sep 16, 2015 at 19:57 UTC

    Like you, I do not readily see what your reviewer was on about ... your approach superficially seems like a good and clever one to me.   However, I would proceed to ask the reviewer what s/he meant.

    Intuitively, I would construct the function-call that you pass around as a closure, which Perl supports very well.   Now, the function-reference that you are passing around will invoke the desired function complete with its arguments.

    And yes ... “profile it.”   If the code that you have written “ran fine,” and if you can demonstrate that it will continue to do so under actual load, then that ... to me ... is a pretty darned powerful argument in favor of what you have presently done.   If the code as-written consumes an inconsequential amount of microseconds and megabytes, that’s another feather for it.   Nevertheless, the first person with whom I would discuss the matter ... and, listen very carefully ... is the colleague who gave a dissenting review.   Presume that s/he sees something.

Re: Cost of passing function references
by sportsfan33 (Initiate) on Sep 17, 2015 at 14:24 UTC

    Everyone,

    Thanks a bunch for very helpful responses. After talking to the reviewer, it turned out that he was thinking along the lines of adding one more layer of "eval" in the code. The line of thought was something like this (I may not be capturing it correctly):

    1. Perl interpretes (compiles?) the code as it sees it. I am not sure at this point if perl creates an executable or a bytecode of the module. The reviewer thinks that the interpreted code is just one big buffer of string.

    2. If a function is used as is, the function name exists in the buffer. In that case, executing the code would involve just looking up the function by its name in the process memory space and executing it.

    3. If a reference is used, the interpreted string buffer needs to be "reinterpreted" (or recompiled?) by substituting the function name in place of the reference. Since perl does not have an executable, each time the CGI comes in from the backend, this process is repeated for the module, which can add a significant overhead to the server side execution. Since I am changing a core library which will eventually lie in the path of our entire UI, the UI can get slower. Even a 5% slowdown would be very annoying.

    I agree that I do not know perl. A bit embarrassed actually, because I have no clue how the perl code is executed. I read in the responses that perl code is compiled, but I don't know what's the result of that compilation. Is the compiled code loaded in memory (if I read one of the responses correctly, looks like it is loaded in some kind of "glob" structure?) and then executed? What happens if there is thousands of lines of code in a perl module? Is everything loaded in memory? Will that cause some memory issues?

    We mutually agreed that for now, we do not need the pattern, and since I had to call a couple of DBI functions only, it was better to wrap those 2 functions separately in the try-catch-throw pattern and call the wrappers instead. If the number of functions we need to call in that way start to increase, we will revisit the pattern.

    I would appreciate more insights on perl code execution and good resources I can look into. Again, thanks a bunch for all the help!

      Nice blog post describing the process can be found here. For more details, read Programming Perl, Chapter 16 "Compiling" in the 4th edition.
      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ