Skip to content

Conversation

@jb55
Copy link
Contributor

@jb55 jb55 commented May 2, 2020

In my investigations into heap allocations during IBD, I discovered that ThreadRename is called many many times, in some cases it does multiple string allocations as well. This modifies ThreadRename to not allocate memory, opting for a static thread-local buffer instead.

I also create a new ThreadRenameWithWorker helper to avoid strprintf allocations.

before:
May02-001641

after:
May02-002048

This is called many times, let's use a buffer on the stack instead
@jb55 jb55 force-pushed the 2020-05-static-threadrename branch from 4cc45a9 to 9d7c4c4 Compare May 2, 2020 07:29
@jb55 jb55 mentioned this pull request May 2, 2020
4 tasks
@practicalswift
Copy link
Contributor

Why is ThreadRename being called so many times? Does it make sense? :)

@hebasto
Copy link
Member

hebasto commented May 2, 2020

... ThreadRename is called many many times...

It seems like a more serious bug than memory over-allocation.

@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

hmmm I went into this from a top-down approach. I'm not familiar with the internals of core, but it's possible the tool I'm using (heaptrack) might have false positives or be flat out wrong. all I know is that doing this PR made the ThreadRename heaptrack logs go away.

There are a bunch of callsites but none of them look too suspicious...

@laanwj
Copy link
Member

laanwj commented May 2, 2020

Uhm, if this is that case, the underlying issue must be addressed, not the symptom. It's supposed to only be called at initial thread creation and bitcoin core's threads are more or less static.
(e.g. the HTTP worker threads are only supposed to be created once, at startup, not all the time? wtf is happening lately)

Could this also explain why thread_local adds unexpected amount of overhead? (see #18678)

@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

is there some mechanism where thread jobs would allocate and destroy thread_local heap storage every time or something? I'm not that familiar with thread_local...

@laanwj
Copy link
Member

laanwj commented May 2, 2020

It's somewhat of a mystery. It's up to the compiler - OS - libc - architecture combination how thread_local is implemented.

Could you try --disable-threadlocal to see if it makes this problem go away?

@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

amazingly, the allocs went away with --disable-threadlocal

May02-021545

@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

going to make clean just incase to confirm

@laanwj
Copy link
Member

laanwj commented May 2, 2020

Ouch. Looks like you're on to something.

Which platform are you checking on? On x86_64, Ubuntu 20.04, with gcc 9.3.0-10ubuntu2, it looks like ThreadRename is only called once per thread as expected, even with active RPC use (edit: oh and once at shutdown with shutoff). Checked by breakpointing it in gdb.

@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

yup no more ThreadRename allocs on master with --disable-threadlocal after a make clean

gcc (GCC) 9.3.0

May02-022408

going to try with clang

@laanwj
Copy link
Member

laanwj commented May 2, 2020

I have the same. Why can't I reproduce this. This is fucking madness.

Edit: can you please trap the function in gdb and look what the backtrace is, where it keeps being called from for the same threads?

@hebasto
Copy link
Member

hebasto commented May 2, 2020

Tested IBD on x86_64, Linux Mint 19.3 with the patched util::ThreadRename() to print a message. The only output is:

$ ./src/qt/bitcoin-qt -testnet -server
ThreadRename: name=qt-init
ThreadRename: name=scriptch.1
ThreadRename: name=scriptch.0
ThreadRename: name=scriptch.2
ThreadRename: name=scheduler
ThreadRename: name=httpworker.0
ThreadRename: name=http
ThreadRename: name=httpworker.3
ThreadRename: name=httpworker.1
ThreadRename: name=httpworker.2
ThreadRename: name=loadblk
ThreadRename: name=net
ThreadRename: name=addcon
ThreadRename: name=msghand
ThreadRename: name=opencon
ThreadRename: name=dnsseed
ThreadRename: name=shutoff

@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

heaptrack tracks allocations, I have no idea how heaptrack attributes mallocs to ThreadRename, but it must be something thread_local related...

here's with clang with thread_local enabled:

May02-023523

let me try to log the get internal name call

@laanwj
Copy link
Member

laanwj commented May 2, 2020

I have the feeling it's more subtle.
Mind that without thread_local enabled, the SetInternalName function called from ThreadRename is stubbed out

static void SetInternalName(std::string name) { }

It might still be that it is called many times, it just doesn't show up in this profile.
(but it means that ::prctl for ex. will still be called a lot)

@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

ok I'm dumb, I think I was reading this pane wrong. It's showing the top-down allocations. so that means it allocated that many times UP to the point it allocated once in ThreadRename.

so perhaps the thread_local performance thing is just because LogPrintF has to allocate a bit more to display thread info? Seems like 6 minutes is lot though just for a print line...

@jb55 jb55 closed this May 2, 2020
@jb55 jb55 reopened this May 2, 2020
@jb55
Copy link
Contributor Author

jb55 commented May 2, 2020

this might still be useful for the case where we're logging thread names... but it's pretty minor compared to other huge sources of allocations. going to close this for now.

@jb55 jb55 closed this May 2, 2020
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants