Re: Ways to make a free variable local to a function?

2018-04-15 Thread Yubin Ruan
On 2018-04-15 13:31, Kirill Balunov wrote:
> 
> 
> 2018-04-15 10:58 GMT+03:00 Yubin Ruan :
> 
> [this is a bit late...]
> 
> Did you really have any benchmark for it? I know what you are doing but it
> seems to be a pre-mature optimization. If this really is the case, then we
> can
> optimize the Python interpreter.
> 
> 
> I don't know if you intentionally send this message privately to me and not to
> the entire python-list. If it was a mistake, post it there and I will answer
> there too.

Sorry I mistakenly hit the 'Reply' button instead of 'Group-reply'.
 
> Yes I've measured:
> 
> 
> def func(numb):
>     res = []
>     for i in range(numb):
>         res.append(int(i) + float(i))
>     return res
> 
> def func_local(numb, _int = int, _float = float):
>     res = []
>     for i in range(numb):
>         res.append(_int(i) + _float(i))
>     return res

Wouldn't this kind of things better be (implicitly) used in the Python
interpreter instead of in this kind of hand-optimized code? That looks
strange.

Nevertheless, if need be, I would opt for that 'static-declared' approach you
described earlier, which give semantic hint on the optimization used.

Thanks,
Yubin
 
> %timeit func(10)
> 
> 85.8 ms ± 2.47 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
> 
> 
> %timeit func_local(10)
> 
> 72.2 ms ± 892 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
> 
> So in the tight loop it is 16% faster.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ways to make a free variable local to a function?

2018-03-06 Thread Chris Angelico
On Tue, Mar 6, 2018 at 8:02 PM, Kirill Balunov  wrote:
>
>
> 2018-03-05 17:34 GMT+03:00 Chris Angelico :
>>
>> In theory, the CPython bytecode compiler (don't know about other
>> Python implementations) could just add these as constants. They'd then
>> be bound at either compile time or function definition time (by
>> default the former, I think, but the latter would be more useful), and
>> be looked up as quickly as locals. I'm not sure how useful this would
>> be, though.
>
>
> With some assumptions, It will be useful for every function call:-)
>
>> If PEP 572 [1] were to be accepted, you could do something like this:
>>
>> def func(numb):
>> if ((int as int), (float as float)):
>> res = []
>> for i in range(numb):
>> res.append(int(i) + float(i))
>> return res
>>
>> Syntactically a bit clunky, but keeps everything inside the function,
>> and DOES create local variables. Not sure it's better than your other
>> options, but it is another option.
>
>
> While I'm +0.5 on yours PEP 572 idea, especially in `while` and `if`
> statements, this example is an overuse of the proposed syntax ;-) Also it
> will add an overhead on every function call, and as you said  -
> "Syntactically a bit clunky".
>

The run-time overhead should be insignificant; this kind of
optimization is done when you're running a tight loop, so it's the run
time of the loop body that dominates the function. That's also why I
do NOT want this to happen at compile time, even though that would be
the easiest. The very latest this should happen is function definition
time; it would be extremely surprising otherwise. And if it happens
once when the function's called, that's usually not going to be much
cost compared to the saving of LOAD_FAST instead of LOAD_GLOBAL in
each iteration of the loop.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ways to make a free variable local to a function?

2018-03-06 Thread Kirill Balunov
 2018-03-05 21:44 GMT+03:00 Terry Reedy :

> Yes, what we really want for this sort of thing are unrebindable local
> constants.  A simple syntax change could do it.
>
>  def func_local_1(numb; int = int, float = float, range = range):
>
> The binding after ';' belong in the header because they should be done
> once.
>
> They'd then
>> be bound at either compile time or function definition time (by
>> default the former, I think, but the latter would be more useful), and
>> be looked up as quickly as locals. I'm not sure how useful this would
>> be, though.
>>
>
> I believe that the occasional practice of re-binding built-in names to
> locals can be shown to speed up loops run enough times.


Yes "_unrebindable local constants_" it is what I was thinking about. But I
do not agree that they must be passed through arguments, because they
should be somewhat static for a function, and could not be changed by any
means after function is compiled.
Alternative option, more dynamic - to allow injecting local variables into
the function via some interface. Currently, there is no such _feature_ in
Python, at least I do not know. There was _somewhat_ related discussion
about how to change the locals of a frame (https://bugs.python.org/
issue1654367) by making `frame.f_locals` writable, but it seems that it is
dead.

With kind regards,
-gdg
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ways to make a free variable local to a function?

2018-03-06 Thread Wolfgang Maier

On 03/05/2018 07:44 PM, Terry Reedy wrote:

On 3/5/2018 9:34 AM, Chris Angelico wrote:

On Tue, Mar 6, 2018 at 12:52 AM, Terry Reedy  wrote:

On 3/5/2018 7:12 AM, Kirill Balunov wrote:

# 1. By passing through local variable's default values

   def func_local_1(numb, _int = int, _float = float, _range = range):



You are not required to mangle the names.

def func_local_1(numb, int = int, float = float, range = range):
...



Even so, this does mess up the function's signature,


Which I why I only said that using the original names solves the syntax
highlighting issue (of marking built-ins as built-ins).


leaving your
callers wondering if they can call it with some sort of range
parameter. (Though in this particular instance, range() is only called
once, so it's pretty much useless to try to optimize it.)

In theory, the CPython bytecode compiler (don't know about other
Python implementations) could just add these as constants.


Yes, what we really want for this sort of thing are unrebindable local
constants.  A simple syntax change could do it.

   def func_local_1(numb; int = int, float = float, range = range):

The binding after ';' belong in the header because they should be done once.



Ah, I did not really understand initially what Kirill was trying to 
achieve by putting the name binding into the function signature.
Now I do, but I don't think it is a good idea. Sanctioning this with 
dedicated syntax would only make Python more static because for any 
function defined this way, you would lose the ability to alter the 
behavior of that function through changing the global binding after the 
function has been called (in the example above, you could no longer mock 
replace int, float and range on subsequent func_local_1 calls) and I 
don't think this is something that should be encouraged.


Wolfgang


--
https://mail.python.org/mailman/listinfo/python-list


Re: Ways to make a free variable local to a function?

2018-03-06 Thread Kirill Balunov
2018-03-05 17:34 GMT+03:00 Chris Angelico :

> In theory, the CPython bytecode compiler (don't know about other
> Python implementations) could just add these as constants. They'd then
> be bound at either compile time or function definition time (by
> default the former, I think, but the latter would be more useful), and
> be looked up as quickly as locals. I'm not sure how useful this would
> be, though.
>

With some assumptions, It will be useful for every function call:-)

If PEP 572 [1] were to be accepted, you could do something like this:
>
> def func(numb):
> if ((int as int), (float as float)):
> res = []
> for i in range(numb):
> res.append(int(i) + float(i))
> return res
>
> Syntactically a bit clunky, but keeps everything inside the function,
> and DOES create local variables. Not sure it's better than your other
> options, but it is another option.
>

While I'm +0.5 on yours PEP 572 idea, especially in `while` and `if`
statements, this example is an overuse of the proposed syntax ;-) Also it
will add an overhead on every function call, and as you said  -
"Syntactically a bit clunky".

With kind regards,
-gdg
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ways to make a free variable local to a function?

2018-03-05 Thread Terry Reedy

On 3/5/2018 9:34 AM, Chris Angelico wrote:

On Tue, Mar 6, 2018 at 12:52 AM, Terry Reedy  wrote:

On 3/5/2018 7:12 AM, Kirill Balunov wrote:

# 1. By passing through local variable's default values

  def func_local_1(numb, _int = int, _float = float, _range = range):



You are not required to mangle the names.

def func_local_1(numb, int = int, float = float, range = range):
...



Even so, this does mess up the function's signature,


Which I why I only said that using the original names solves the syntax 
highlighting issue (of marking built-ins as built-ins).



leaving your
callers wondering if they can call it with some sort of range
parameter. (Though in this particular instance, range() is only called
once, so it's pretty much useless to try to optimize it.)

In theory, the CPython bytecode compiler (don't know about other
Python implementations) could just add these as constants.  


Yes, what we really want for this sort of thing are unrebindable local 
constants.  A simple syntax change could do it.


 def func_local_1(numb; int = int, float = float, range = range):

The binding after ';' belong in the header because they should be done once.


They'd then
be bound at either compile time or function definition time (by
default the former, I think, but the latter would be more useful), and
be looked up as quickly as locals. I'm not sure how useful this would
be, though.


I believe that the occasional practice of re-binding built-in names to 
locals can be shown to speed up loops run enough times.



If PEP 572 [1] were to be accepted, you could do something like this:

def func(numb):
 if ((int as int), (float as float)):
 res = []
 for i in range(numb):
 res.append(int(i) + float(i))
 return res

Syntactically a bit clunky, but keeps everything inside the function,
and DOES create local variables. Not sure it's better than your other
options, but it is another option.


Code in the body should be executed everytime the function is called. 
Those binding should not be as they only need to be done once.



[1] PEP 572: https://www.python.org/dev/peps/pep-0572/


--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list


Re: Ways to make a free variable local to a function?

2018-03-05 Thread Chris Angelico
On Tue, Mar 6, 2018 at 12:52 AM, Terry Reedy  wrote:
> On 3/5/2018 7:12 AM, Kirill Balunov wrote:
>> # 1. By passing through local variable's default values
>>
>>  def func_local_1(numb, _int = int, _float = float, _range = range):
>
>
> You are not required to mangle the names.
>
> def func_local_1(numb, int = int, float = float, range = range):
> ...
>

Even so, this does mess up the function's signature, leaving your
callers wondering if they can call it with some sort of range
parameter. (Though in this particular instance, range() is only called
once, so it's pretty much useless to try to optimize it.)

In theory, the CPython bytecode compiler (don't know about other
Python implementations) could just add these as constants. They'd then
be bound at either compile time or function definition time (by
default the former, I think, but the latter would be more useful), and
be looked up as quickly as locals. I'm not sure how useful this would
be, though.

If PEP 572 [1] were to be accepted, you could do something like this:

def func(numb):
if ((int as int), (float as float)):
res = []
for i in range(numb):
res.append(int(i) + float(i))
return res

Syntactically a bit clunky, but keeps everything inside the function,
and DOES create local variables. Not sure it's better than your other
options, but it is another option.

[1] PEP 572: https://www.python.org/dev/peps/pep-0572/

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ways to make a free variable local to a function?

2018-03-05 Thread Terry Reedy

On 3/5/2018 7:12 AM, Kirill Balunov wrote:

Hi,

At the moment, in order to slightly speed up the function in Python, free
variables are passed as local variables to the function, thereby getting
rid of extra look ups. For example, for the following function, I
especially do not use list comprehension) and therefore maybe it's not the
best example:

 def func(numb):
 res = []
 for i in range(numb):
 res.append(int(i) + float(i))
 return res

You can get rid of additional look ups, in the following ways:


# 1. By passing through local variable's default values

 def func_local_1(numb, _int = int, _float = float, _range = range):


You are not required to mangle the names.

def func_local_1(numb, int = int, float = float, range = range):
...


 res = []
 for i in _range(numb):
 res.append(_int(i) + _float(i))
 return res


# 2. Through importing them into the function scope

 def func_local_2(numb):
 from builtins import int, float, range
 res = []
 for i in range(numb):
 res.append(int(i) + float(i))
 return res


# 3. With the help of various types of closures, version 1

 def func_closure_1(numb):
 _int = int
 _float = float
 _range = range
 def inner(numb):
 res = []
 for i in _range(numb):
 res.append(_int(i) + _float(i))
 return res
 return inner(numb)


# 4. With the help of various types of closures, version 2

 def func_closure_2(numb):
 from builtins import int, float, range
 def inner(numb):
 res = []
 for i in range(numb):
 res.append(int(i) + float(i))
 return res
 return inner(numb)

Option 1 allows you to achieve the maximum result for both small and a
large `numb` values. Option 2 yields a significant overhead, when it is
required to call function many times with a small number of iterations. For
option 3 and 4, notes are the same, but since they are implemented through
closures they give additional small overhead. In case of big `numb` (many
iterations, many look ups) these options give a gain of ~10%.

Option 1 and 3 I do not like because:
  - syntax highlighting stops working


Only because you unnecessarily mangle the names.


  - the signature function is greatly distorted
  - additional typing (especially with type annotations)

I like options 2 and 4, but they yield a significant overhead, for a small
number of iterations.

Actually, I have the following question:

1. Is there any other way to make the variable local to the function?
  a. When you compile (define) a function...
  b. Inject into an already existing function through decorator...(is it
possible?)

p.s.:

I had the following idea, maybe it was already discussed, the possibility
of setting _static_ variables for functions, with the following syntax:

 def func(numb):
 static int, float, range
 res = []
 for i in range(numb):
 res.append(int(i) + float(i))
 return res

Where identifiers for `static` should correspond to free variables for a
function, they must be defined at compile time (like for default arguments)
and can not be changed inside the function scope.

With kind regards,
-gdg




--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list