The Cost of Dynamism (was Re: Pyhon 2.x or 3.x, which is faster?)

BartC bc at freeuk.com
Mon Mar 14 09:00:14 EDT 2016


On 14/03/2016 03:01, Steven D'Aprano wrote:
> On Mon, 14 Mar 2016 12:16 am, BartC wrote:

>> Worth having but at significant cost. But look at my jpeg test (look at
>> any other similar programs); how many function names are used
>> dynamically? How many functions *really* need to be dynamic?
>
> As difficult as it may seem sometimes, Python is not a specialist
> programming language designed to be optimal for your jpeg test, it is a
> general purpose programming language designed for many different uses :-)

jpeg is an example of the sort of program that many, many people are 
writing. The sort where you write a bunch of functions, and you write 
code that calls them.

Sometimes, you need to call a function indirectly, or need a table of 
functions, or need to pass a function as an argument, but that's 
possible /without having to rename functions/. Even C can do all this.

>> there seems to be just one kind. The
>> different categories (function, variable, class, built-in, module etc)
>> are sorted out at *run-time*.
>
> What do you mean by "sorted out"?

Well, in the case of executing f(a,b,x=c), then runtime will do the 
lookup of f, check the number of arguments, sort out that keyword 
argument, all that stuff.

That could be done at compile-time (ie. the byte-code compiler) if the 
compiler had sight of the definition of f, and it knew for sure the f in 
f() referred to that definition.

 > You have names in a namespace which are
 > bound to objects. Once you have bound a name, you can rebind it:

> At compile time, you don't in general know what value things will have. You
> don't even know what type they will have (since the type of an object is
> part of its value).

That's true of *variables*. It needn't be true of *functions* and 
*modules* and *classes* because you've told the compiler exactly what 
they are!

If you want to do this with functions, I've already shown a few times a 
technique where you split the function name used with 'def' into two: a 
fixed static name and a dynamic name, a 'pointer' initially referring to 
this function, that can later be assigned something else. If you have to.

> Here is an extreme example to demonstrate the difficulty:
>
>
> import random
> if random.random() < 0.5:
>      def f():
>          return 1
> else:
>      f = "hello"
>
> f = "goodbye"
>
>
> So tell me: *at compile time*, how do you know whether the final binding
> (assignment of "goodbye" to variable f) should be allowed or not?

First, let me try and emulate that exact behaviour in a 'crippled' 
language that doesn't allow function rebinding:

function f1= {return 1}

if random(1)<0.5 then
   f:=f1
else
   f:="hello
fi

f:="goodbye"


So the question doesn't come up; we're simply dealing with variables. 
The actual function (the name that permanently refers to the code 
'return 1', or 'f1' in my example) doesn't change.

> I don't think you are, but let's suppose you're right and that the compiler
> can reliably detect and prevent code like this:
>
> def f(): return 1
> g = f
> g = something_else  # allowed
> f = something_else  # not allowed

Are we talking about some imaginary version of Python where the ability 
to re-bind function, module and class names hadn't been allowed?

Then you would simply tweak the code so that 'f' the variable and 'f' 
the actual function are different names.

Or how you change Python now to have both fixed function names yet allow 
all these manipulations?

I don't know. People don't seem to like adding annotations such as:

static def f(): return 1

which would force you to create an alias for f as I said above:

static def _f(): return 1
f = _f

> How do you intend to prevent this?
>
> def f(): return 1
> exec "f = something_else"

(I don't use exec or eval, and have never seriously implemented them. In 
fact I wrote these comments once in comp.programming a few years back:

 >> Perhaps an "Eval Index" can be created which measures how much Eval
 >> slows down a language: the bigger the slow-down, then the more
 >> efficient would be the language. And it could be measured by putting
 >> Eval "...." around every line or statement (or smallest unit of
 >> which Eval can make sense) of a benchmark program and seeing how
 >> much it slows down.
 >>
 >> (And something like Lisp I'd imagine would have an Eval Index of 1.0
 >> :-)  )

But to get back to your question: same answer as above.

> Suppose we removed the ability to rebind names that were bound to a
> function, as you suggest. There's a very important and commonplace use for
> rebinding functions that we would lose: decorators.

>But there are times where you
> cannot use decorator syntax, and have to write it the old-school way:
>
> func = decorate(func)

I can't see a problem here. Just a minor bit of renaming needed. 
(Although I don't understand whatever else decorators too.)

Yes, there's a bit of inconvenience when you're importing a library 
where you're not allowed to re-bind the functions inside it, so that you 
can use the same name to mean something different.

But how many programs need that functionality? It might be a case of the 
tail wagging the dog...

And there will generally be a way around it.

>> I think anyway that any Python program using dynamic functions, can be
>> trivially transformed to one that uses static functions. It won't be
>> pretty, but any function:
>>
>>    def f(): whatever
>>
>> could be rewritten as:
>>
>>    def __f(): whatever
>>    f = __f()

> No it can't. You haven't thought it through.

> (1) Are you expecting to write "__f()" inside your code when you mean to
> call f()? If so, then what's the purpose of f()? If not, then you
> write "f()" and nothing has changed: the interpreter has to do a runtime
> lookup because f might have changed.

> (2) What is stopping people from changing __f in all the many different ways
> that it could be changed?

The above is simply to demonstrate that a program that relies on writing 
to function names (ie. the names that follow 'def') can be transformed 
into an equivalent program where such a name doesn't need writing to.

Ergo, Python doesn't /need/ mutable function names. (Disclaimer: this is 
conjecture on my part...)

I'm not suggesting that people now have to start writing these aliases 
everywhere (that would be needed in more languages that foolishly 
separate the concept of a function, from a reference to a function).

It just opens up the possibility of such static functions which can lead 
to more streamlined or more easily optimisable code.

> Now that I've shown you all the ways that code can be changed on the fly,
> and probably convinced you that it is impossible to optimize Python without
> changing the language,

I'm sure it can. Projects such as PyPy have shown that, and on certain 
programs that can give dramatic increases in performance far beyond what 
is possibly by tinkering with CPython.

But because I do language design, I can see many possibilities from 
changing the language too!

-- 
Bartc



More information about the Python-list mailing list