Skip to content
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

perf: Remove all Cgo handles #1073

Merged
merged 9 commits into from
Oct 9, 2024
Merged

Conversation

AlliBalliBaba
Copy link
Collaborator

This PR aims to improve handle performance as discussed in #934

Instead of optimizing handles, they are completely removed and replaced by passing the index of the current thread
This makes it so we can store a slice of phpThreads on the go side and have more potential control over thread
creation/destruction/metrics.

The performance impacts are similar to #934.

A quick worker benchmark:
wrk -t 4 -c 150 -d 60 on WSL bookworm-Docker 20 CPU cores 40 threads

without this PR: ~57000 req/s
with this PR: ~65000 req/s

Note that this benchmark was done with php_import_environment_variables disabled (I plan to optimize that it in a separate PR).

This PR comes with some significant refactoring (especially in worker.go) so I hope it doesn't conflict too much with other potential PRs.

@AlliBalliBaba
Copy link
Collaborator Author

I realized that you can also create go flamegraphs with go tool pprof. I'm creating them like this in the dev.Dockerfile:

# installation
mkdir /usr/local/src/flamegraph && \
cd /usr/local/src/flamegraph && \
git clone https://github.com/brendangregg/FlameGraph.git && \
cd /usr/local/src/flamegraph/FlameGraph

# create a 20s flamegraph.svg in project dir
go tool pprof -raw -output=cpu.txt 'http://localhost:2019/debug/pprof/profile?seconds=20' && \
./stackcollapse-go.pl cpu.txt | ./flamegraph.pl > /go/src/app/flame.svg

Flamegraph after this PR:
flame-no-handles
(M12 comes from import_environment_variables and scales linearly with the amount of environment variables)

@AlliBalliBaba
Copy link
Collaborator Author

AlliBalliBaba commented Oct 7, 2024

@withinboredom I also noticed that with the current settings of minWorkerErrorBackoff=10ms the failing-worker.php test will sometimes crash due to consuming too much memory ('core dump', also in the main branch).

Changing this to 100ms fixes the issue. Still it's weird that it would start consuming so much memory, maybe it's spawning too many goroutines?

Copy link
Owner

@dunglas dunglas left a comment

Choose a reason for hiding this comment

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

Awesome! Very good idea.

frankenphp.go Outdated
@@ -517,25 +502,25 @@ func ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) error
}

//export go_handle_request
func go_handle_request() bool {
func go_handle_request(threadIndex int) bool {
Copy link
Owner

Choose a reason for hiding this comment

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

Shouldn't we use uint64_t or uint32_t everywhere? (C-side and Go side)? The current implementation may trigger incompatibilities between C and Go types if a (very) large number of threads is started.

Suggested change
func go_handle_request(threadIndex int) bool {
func go_handle_request(threadIndex C.uint64_t) bool {

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was wondering how go treats the integers coming from C since they don't seem to need casting. I changed it to uint32_t since I'm not sure how uint64_t would be cast on 32 bit systems.

Copy link
Owner

Choose a reason for hiding this comment

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

After thinking about this a bit more, maybe the best option is C.uintptr_t. As Go's int type, it will have a different size depending on the platform capabilities, and it will prevent a conversion C-side.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should it then be a map of C.uintptr_t? A slice still works, but it somehow feels weird to do this: phpThreads[C.uintptr_t(i)]

Copy link
Owner

Choose a reason for hiding this comment

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

it shouldn't be an issue

php_thread.go Outdated Show resolved Hide resolved
php_thread.go Outdated Show resolved Hide resolved
@dunglas
Copy link
Owner

dunglas commented Oct 7, 2024

By the way, I would like to contact you privately @AlliBalliBaba, I couldn't find a way to contact you, would you mind sending a mail or a DM on any social network?

@dunglas
Copy link
Owner

dunglas commented Oct 8, 2024

@AlliBalliBaba I simplified the code, rebased and renamed a function to match our CS in 756675f (#1073). Let's merge if it works for you.

@AlliBalliBaba
Copy link
Collaborator Author

Sounds good to me 👍

@dunglas dunglas merged commit d99b16a into dunglas:main Oct 9, 2024
32 checks passed
@dunglas
Copy link
Owner

dunglas commented Oct 9, 2024

Thank you @AlliBalliBaba!!

@AlliBalliBaba AlliBalliBaba deleted the no-cgo-handles branch October 9, 2024 06:36
@AlliBalliBaba
Copy link
Collaborator Author

@dunglas fyi I sent you mail on [email protected]

@dunglas dunglas mentioned this pull request Oct 18, 2024
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