-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
bpo-27535: Fix memory leak with warnings ignore #4489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to play with the code more.
Python/_warnings.c
Outdated
else if (_PyUnicode_EqualToASCIIString(action, "ignore")) | ||
} | ||
|
||
if (_PyUnicode_EqualToASCIIString(action, "ignore")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this already tested above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed it. I kept it for tests, but then I forgot to remove it.
Python/_warnings.c
Outdated
/* Store in the registry that we've been here, *except* when the action | ||
is "always". */ | ||
rc = 0; | ||
if (!_PyUnicode_EqualToASCIIString(action, "always")) { | ||
if (registry != NULL && registry != Py_None && | ||
PyDict_SetItem(registry, key, Py_True) < 0) | ||
PyDict_SetItem(registry, key, Py_True) < 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to add braces, place the opening brace after a multiline condition on a separate line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PEP 7 only shows "if (test) {", it says nothing about multiline condition. Until a newline is required by the PEP 7, I prefer to put "{" on the same line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* When you break a long expression at a binary operator, the
operator goes at the end of the previous line, and braces should be
formatted as shown. E.g.::
if (type->tp_dictoffset != 0 && base->tp_dictoffset == 0 &&
type->tp_dictoffset == b_size &&
(size_t)t_size == b_size + sizeof(PyObject *))
{
return 0; /* "Forgive" adding a __dict__ only */
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh nice, it's specified in the PEP 7. In that case, ok to reformat the code as the PEP 7 requires ;-)
Lib/warnings.py
Outdated
re.compile(module), lineno, append=append) | ||
|
||
if message: | ||
regex = re.compile(message, re.I) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be written shorter.
regex = re.compile(message, re.I) if message else None
module = re.compile(module) if module else None
_add_filter(action, regex, category, module, lineno, append=append)
Lib/test/test_warnings/__init__.py
Outdated
@@ -125,6 +125,8 @@ def test_ignore(self): | |||
self.module.filterwarnings("ignore", category=UserWarning) | |||
self.module.warn("FilterTests.test_ignore", UserWarning) | |||
self.assertEqual(len(w), 0) | |||
self.assertEqual(list(__warningregistry__.keys()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line doesn't look too long for splitting it. Especially if remove redundant .keys()
.
Ooops, previously, I pushed optimization commits in this PR by mistake. It's now fixed. Again, this PR is only the change to leave the registry unchanged for the "ignore" action. |
The warnings module doesn't leak memory anymore in the hidden warnings registry for the "ignore" action of warnings filters. The warn_explicit() function doesn't add the warning key to the registry anymore for the "ignore" action.
I reformatted the if with multiline condition and rebased my PR. |
Latest reliable (using isolated CPUs) benchmark:
So this PR adds a slowdown of +133 ns on warning.warn() when the warning is ignored. This slowdown can be removed using a "C cache" of warnings.filters: the optimization implemented in PR #4502. The problem is that I'm not sure that it's ok to cache warnings.filters and/or make warnings.filters "immutable" (convert it to a tuple). |
The overhead depends on the number and kind of filters. For warnings emitted in a C code the relative slowdown will be larger. |
@serhiy-storchaka: Are you ok to merge this PR even with the "1.2x slower" (+19%)? Or do you prefer to wait until a decision can be made on a "C cache" for warnings.filters? |
Could you please make a benchmark for warnings emitted in a tight loop in the C code. It would be nice to know what is the worst case. If it is less than 2x slower I will prefer this simpler PR. |
It's not 2x slower, it's between 1.03x slower (+21 ns) and 1.09x slower (+79 ns): |
Thanks @vstinner for the PR 🌮🎉.. I'm working now to backport this PR to: 3.6. |
The warnings module doesn't leak memory anymore in the hidden warnings registry for the "ignore" action of warnings filters. The warn_explicit() function doesn't add the warning key to the registry anymore for the "ignore" action. (cherry picked from commit c975878)
GH-4587 is a backport of this pull request to the 3.6 branch. |
I backported manually the change to Python 2.7: PR #4588. |
The warnings module doesn't leak memory anymore in the hidden
warnings registry for the "ignore" action of warnings filters.
The warn_explicit() function doesn't add the warning key to the
registry anymore for the "ignore" action.
https://bugs.python.org/issue27535