Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gevent
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
gevent
Commits
d51c1c07
Commit
d51c1c07
authored
Jan 25, 2018
by
Jason Madden
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Put gevent.local on a diet: remove vtables by using functions, not methods
parent
1910b1d6
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
131 additions
and
100 deletions
+131
-100
CHANGES.rst
CHANGES.rst
+5
-3
src/gevent/local.pxd
src/gevent/local.pxd
+22
-4
src/gevent/local.py
src/gevent/local.py
+104
-93
No files found.
CHANGES.rst
View file @
d51c1c07
...
...
@@ -75,9 +75,11 @@ Build Changes
- :class:`gevent.local.local` is compiled with Cython on CPython. It
was already 5 to 6 times faster due to the work on :issue:`1020`,
and compiling it with Cython makes it another 5 to 6 times faster, for a
total speed up of about 35 times. It is now in the same ballpark as
the native :class:`threading.local` class. See :pr:`1024`.
and compiling it with Cython makes it another 5 to 6 times faster,
for a total speed up of about 35 times. It is now in the same
ballpark as the native :class:`threading.local` class. It also uses
one pointer less memory per object, and one pointer less memory per
greenlet. See :pr:`1024`.
Other Changes
...
...
src/gevent/local.pxd
View file @
d51c1c07
...
...
@@ -7,18 +7,36 @@ cimport cython
cdef
class
_wrefdict
(
dict
):
cdef
object
__weakref__
@
cython
.
final
@
cython
.
internal
cdef
class
_thread_deleted
:
cdef
object
idt
cdef
object
wrdicts
@
cython
.
final
@
cython
.
internal
cdef
class
_local_deleted
:
cdef
str
key
cdef
object
wrthread
cdef
_thread_deleted
thread_deleted
@
cython
.
final
@
cython
.
internal
cdef
class
_localimpl
:
cdef
str
key
cdef
dict
dicts
cdef
_wref
dict
dicts
cdef
tuple
localargs
cdef
object
__weakref__
cdef
dict
create_dict
(
self
)
cdef
dict
get_dict
(
self
)
cdef
dict
_localimpl_create_dict
(
_localimpl
self
)
cdef
inline
dict
_localimpl_get_dict
(
_localimpl
self
)
cdef
class
local
:
cdef
_localimpl
_local__impl
cdef
_local__copy_dict_from
(
self
,
_localimpl
impl
,
dict
duplicate
)
cdef
inline
dict
_local_get_dict
(
local
self
)
cdef
_local__copy_dict_from
(
local
self
,
_localimpl
impl
,
dict
duplicate
)
src/gevent/local.py
View file @
d51c1c07
...
...
@@ -163,6 +163,37 @@ __all__ = ["local"]
class
_wrefdict
(
dict
):
"""A dict that can be weak referenced"""
class
_thread_deleted
(
object
):
__slots__
=
(
'idt'
,
'wrdicts'
)
def
__init__
(
self
,
idt
,
wrdicts
):
self
.
idt
=
idt
self
.
wrdicts
=
wrdicts
def
__call__
(
self
,
_unused
):
dicts
=
self
.
wrdicts
()
if
dicts
:
dicts
.
pop
(
self
.
idt
,
None
)
class
_local_deleted
(
object
):
__slots__
=
(
'key'
,
'wrthread'
,
'thread_deleted'
)
def
__init__
(
self
,
key
,
wrthread
,
thread_deleted
):
self
.
key
=
key
self
.
wrthread
=
wrthread
self
.
thread_deleted
=
thread_deleted
def
__call__
(
self
,
_unused
):
thread
=
self
.
wrthread
()
if
thread
is
not
None
:
try
:
unlink
=
thread
.
unlink
except
AttributeError
:
pass
else
:
unlink
(
self
.
thread_deleted
)
del
thread
.
__dict__
[
self
.
key
]
class
_localimpl
(
object
):
"""A class managing thread-local dicts"""
__slots__
=
(
'key'
,
'dicts'
,
'localargs'
,
'__weakref__'
,)
...
...
@@ -179,66 +210,72 @@ class _localimpl(object):
# We need to create the thread dict in anticipation of
# __init__ being called, to make sure we don't call it
# again ourselves. MUST do this before setting any attributes.
self
.
create_dict
()
def
get_dict
(
self
):
"""Return the dict for the current thread. Raises KeyError if none
defined."""
thread
=
getcurrent
()
return
self
.
dicts
[
id
(
thread
)][
1
]
def
create_dict
(
self
):
"""Create a new dict for the current thread, and return it."""
localdict
=
{}
key
=
self
.
key
thread
=
getcurrent
()
idt
=
id
(
thread
)
wrdicts
=
ref
(
self
.
dicts
)
def
thread_deleted
(
_
,
idt
=
idt
,
wrdicts
=
wrdicts
):
# When the thread is deleted, remove the local dict.
# Note that this is suboptimal if the thread object gets
# caught in a reference loop. We would like to be called
# as soon as the OS-level thread ends instead.
# If we are working with a gevent.greenlet.Greenlet, we
# can pro-actively clear out with a link, avoiding the
# issue described above.Use rawlink to avoid spawning any
# more greenlets.
dicts
=
wrdicts
()
if
dicts
:
dicts
.
pop
(
idt
,
None
)
_localimpl_create_dict
(
self
)
# We use functions instead of methods so that they can be cdef'd in
# local.pxd; if they were cdef'd as methods, they would cause
# the creation of a pointer and a vtable. This happens
# even if we declare the class @cython.final. functions thus save memory overhead
# (but not pointer chasing overhead; the vtable isn't used when we declare
# the class final).
def
_localimpl_get_dict
(
self
):
"""Return the dict for the current thread. Raises KeyError if none
defined."""
thread
=
getcurrent
()
return
self
.
dicts
[
id
(
thread
)][
1
]
def
_localimpl_create_dict
(
self
):
"""Create a new dict for the current thread, and return it."""
localdict
=
{}
key
=
self
.
key
thread
=
getcurrent
()
idt
=
id
(
thread
)
wrdicts
=
ref
(
self
.
dicts
)
# When the thread is deleted, remove the local dict.
# Note that this is suboptimal if the thread object gets
# caught in a reference loop. We would like to be called
# as soon as the OS-level thread ends instead.
# If we are working with a gevent.greenlet.Greenlet, we
# can pro-actively clear out with a link, avoiding the
# issue described above.Use rawlink to avoid spawning any
# more greenlets.
thread_deleted
=
_thread_deleted
(
idt
,
wrdicts
)
try
:
rawlink
=
thread
.
rawlink
except
AttributeError
:
wrthread
=
ref
(
thread
,
thread_deleted
)
else
:
rawlink
(
thread_deleted
)
wrthread
=
ref
(
thread
)
try
:
rawlink
=
thread
.
rawlink
except
AttributeError
:
wrthread
=
ref
(
thread
,
thread_deleted
)
else
:
rawlink
(
thread_deleted
)
wrthread
=
ref
(
thread
)
def
local_deleted
(
_
,
key
=
key
,
wrthread
=
wrthread
,
thread_deleted
=
thread_deleted
):
# When the localimpl is deleted, remove the thread attribute.
thread
=
wrthread
()
if
thread
is
not
None
:
try
:
unlink
=
thread
.
unlink
except
AttributeError
:
pass
else
:
unlink
(
thread_deleted
)
del
thread
.
__dict__
[
key
]
wrlocal
=
ref
(
self
,
local_deleted
)
thread
.
__dict__
[
key
]
=
wrlocal
self
.
dicts
[
idt
]
=
wrthread
,
localdict
return
localdict
# When the localimpl is deleted, remove the thread attribute.
local_deleted
=
_local_deleted
(
key
,
wrthread
,
thread_deleted
)
wrlocal
=
ref
(
self
,
local_deleted
)
thread
.
__dict__
[
key
]
=
wrlocal
self
.
dicts
[
idt
]
=
wrthread
,
localdict
return
localdict
_marker
=
object
()
def
_local_get_dict
(
self
):
impl
=
self
.
_local__impl
try
:
dct
=
_localimpl_get_dict
(
impl
)
except
KeyError
:
dct
=
_localimpl_create_dict
(
impl
)
args
,
kw
=
impl
.
localargs
self
.
__init__
(
*
args
,
**
kw
)
return
dct
class
local
(
object
):
"""
An object whose attributes are greenlet-local.
...
...
@@ -259,17 +296,7 @@ class local(object):
# they will be, and this will produce an error.
return
object
.
__getattribute__
(
self
,
name
)
# Begin inlined function _get_dict()
# Using object.__getattribute__ here disables Cython knowing the
# type of the object and using cdef calls to it.
impl
=
self
.
_local__impl
try
:
dct
=
impl
.
get_dict
()
except
KeyError
:
dct
=
impl
.
create_dict
()
args
,
kw
=
impl
.
localargs
self
.
__init__
(
*
args
,
**
kw
)
# End inlined function _get_dict
dct
=
_local_get_dict
(
self
)
if
name
==
'__dict__'
:
return
dct
...
...
@@ -348,16 +375,7 @@ class local(object):
object
.
__setattr__
(
self
,
'_local__impl'
,
value
)
return
# Begin inlined function _get_dict()
impl
=
self
.
_local__impl
try
:
dct
=
impl
.
get_dict
()
except
KeyError
:
dct
=
impl
.
create_dict
()
args
,
kw
=
impl
.
localargs
self
.
__init__
(
*
args
,
**
kw
)
# End inlined function _get_dict
dct
=
_local_get_dict
(
self
)
type_self
=
type
(
self
)
if
type_self
is
local
:
...
...
@@ -393,14 +411,7 @@ class local(object):
# Otherwise it goes directly in the dict
# Begin inlined function _get_dict()
impl
=
self
.
_local__impl
try
:
dct
=
impl
.
get_dict
()
except
KeyError
:
dct
=
impl
.
create_dict
()
args
,
kw
=
impl
.
localargs
self
.
__init__
(
*
args
,
**
kw
)
# End inlined function _get_dict
dct
=
_local_get_dict
(
self
)
try
:
del
dct
[
name
]
...
...
@@ -410,22 +421,22 @@ class local(object):
def
__copy__
(
self
):
impl
=
self
.
_local__impl
d
=
impl
.
get_dict
(
)
d
=
_localimpl_get_dict
(
impl
)
duplicate
=
copy
(
d
)
cls
=
type
(
self
)
args
,
kw
=
impl
.
localargs
instance
=
cls
(
*
args
,
**
kw
)
local
.
_local__copy_dict_from
(
instance
,
impl
,
duplicate
)
_local__copy_dict_from
(
instance
,
impl
,
duplicate
)
return
instance
def
_local__copy_dict_from
(
self
,
impl
,
duplicate
):
current
=
getcurrent
()
currentId
=
id
(
current
)
new_impl
=
self
.
_local__impl
assert
new_impl
is
not
impl
tpl
=
new_impl
.
dicts
[
currentId
]
new_impl
.
dicts
[
currentId
]
=
(
tpl
[
0
],
duplicate
)
def
_local__copy_dict_from
(
self
,
impl
,
duplicate
):
current
=
getcurrent
()
currentId
=
id
(
current
)
new_impl
=
self
.
_local__impl
assert
new_impl
is
not
impl
tpl
=
new_impl
.
dicts
[
currentId
]
new_impl
.
dicts
[
currentId
]
=
(
tpl
[
0
],
duplicate
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment