Skip to content

Update declarative function#613

Open
frmdstryr wants to merge 3 commits into
nucleic:mainfrom
frmdstryr:update-dfunc
Open

Update declarative function#613
frmdstryr wants to merge 3 commits into
nucleic:mainfrom
frmdstryr:update-dfunc

Conversation

@frmdstryr
Copy link
Copy Markdown
Contributor

Addresses recent issues and updates to use vectorcalls.

A simple timeit calling a dfunc shows about a 30% speedup with this change (but still over order of magnitude slower than a normal py fn call).

I think it is safe to assume the caller "owns" a reference to several of the arguments so this removes a few increfs (pfunc, pself, and argsptr).

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 7, 2026

Codecov Report

❌ Patch coverage is 0% with 65 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.98%. Comparing base (7895e4c) to head (fd8580d).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #613      +/-   ##
==========================================
- Coverage   66.07%   65.98%   -0.09%     
==========================================
  Files         270      270              
  Lines       26584    26617      +33     
  Branches     3901     3914      +13     
==========================================
- Hits        17565    17564       -1     
- Misses       7975     8008      +33     
- Partials     1044     1045       +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@frmdstryr
Copy link
Copy Markdown
Contributor Author

It looks like call_func has to generate a new function with every call. I wonder if the bound dfunc could somehow cache it

@MatthieuDartiailh
Copy link
Copy Markdown
Member

It looks like call_func has to generate a new function with every call. I wonder if the bound dfunc could somehow cache it

Could you point me to this I do not remember why it is so ?

Copy link
Copy Markdown
Member

@MatthieuDartiailh MatthieuDartiailh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question

Comment on lines -483 to -494
PyObject* pymethod;
if( numfree > 0 )
{
pymethod = pyobject_cast( freelist[ --numfree ] );
_Py_NewReference( pymethod );
}
else
{
pymethod = PyType_GenericAlloc( BoundDMethod::TypeObject, 0 );
if( !pymethod )
return 0;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove the freelist ?

Copy link
Copy Markdown
Contributor Author

@frmdstryr frmdstryr Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the first issue said there was issues with GC and suggested doing that and I didn't want to look into it.

But since you asked... I re-added it after digging through methodobject.c and the freelist code that I think simply adding the PyObject_GC_Track fixes the GC issue. The BoundDMethod::TypeObject will still leak references (one per freelist obj) but cant be subclassed so I think that's fine.

@frmdstryr
Copy link
Copy Markdown
Contributor Author

frmdstryr commented Apr 22, 2026

It looks like call_func has to generate a new function with every call. I wonder if the bound dfunc could somehow cache it

Could you point me to this I do not remember why it is so ?

Looking at the source of PyEval_EvalCodeEx it creates a new function using _PyFunction_FromConstructor mostly just copying the arguments.

I'm not sure if it can avoid this and somehow just use _PyEval_Vector which takes the locals. I'll open a PR if I can get something working.

@frmdstryr
Copy link
Copy Markdown
Contributor Author

I noticed PyFunctionObject has it's own func_globals and func_builtins fields (see https://github.com/python/cpython/blob/main/Objects/funcobject.c#L209-L210). Do you see any problems with casting and using those directly (instead of doing getattrs)?

I was able to get func calls to ~3x slower than normal py fn calls (down from 9x slower in current master) using this branch main...frmdstryr:enaml:improve-speed-callfunc but that requires reworking some more things.

@MatthieuDartiailh
Copy link
Copy Markdown
Member

I see no reason why not using the existing fields. I had no time to check your branch but I will try to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants