-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
write(io, ::Int) allocates #39041
Comments
To give a little more color to the issue, here's an example of the GC becoming "overwhelmed"
for me gives output:
Normally, when there are few threads, these tiny allocations are handled fine and you may not even notice. When there's a lot of them, however, it seems like the normal logic in the GC for handling tiny short lived allocations breaks. I think it may be related to this issue: |
Any IO object that has its own internal buffer perhaps should be encouraged to implement something like this:
and it won't allocate. Really this would work for any immutable. TranscodingStreams.jl in particular would benefit from this. Edit: Modulo the alignment issues when writing different length types! |
Could the current allocating implementation
in base/io.jl perhaps be replaced with something along the lines of
Then it wouldn't be necessary any more to wrap “user-defined plain-data types without (Although, |
Sounds good to me. Maybe we should add
to the error message as well. Maybe an |
It's not possible to just convert a bitstype to a pointer like this, you need to put it in some kind of mutable container to get a stable memory address. Maybe we can reuse the same |
Hmm.. I get the reasoning for general isbits types, but primitive types really shouldn't have to be wrapped in a |
Not always, but most of the time. That's exactly the problem, since that means it's not possible to get a stable pointer to them, but the way pipes work, the OS requires a pointer to the data. I don't think there's really any way around that requirement. |
Is there a way to perhaps create something that can iterate the bytes of a bitstype? Its easy to do with an Int or Float etc using bitshifts, so I guess this could be recursively done on simple structs as well. The issue would be handling holes and other alignment issues. |
You still need a pointer one way or another |
What I mean is you only need the value of an Int to iterate all 8 of its bytes, you just shift and mask and cast the value. |
Sure, but |
But many of the cases where unsafe_write is being called, it doesnt end up writing to a system pipe, e.g., the initial example here. All its doing is writing some bytes to a buffer in Julia space, right? |
Yes, |
Could streams be given a trait that indicates that they have their own internal buffer, and dispatch unsafe_write on that? This would apply to IOBuffer and TranscodingStreams and probably many others. |
I suspect the real solution is that the compiler should recognize (possibly supported by a user hint?) that the result of the
is not required beyond the duration of the function call in which it happens (if the I don't really understand under what conditions Julia currently allocates such addresses on the heap or on the stack, and how one can test what it does. See also https://discourse.julialang.org/t/how-to-know-if-object-memory-resides-on-stack-or-heap/4927/12 |
Two approaches possible when a
For using the first approach, function write_iobs2(iobs, data)
Threads.@threads for ii in 1:length(iobs)
for jj in 1:length(data)
write(iobs[ii], Base.RefArray(data, jj))
end
end
end |
@aviatesk can we use the new effect system to help out here, to elide the allocation when it's not necessary? |
I don't think so. I'm wondering if the Line 670 in 895bbc4
|
The heap allocation is necessary for context switch, and the |
Is it conceivable to have a kind of persistent |
Ref #41415 |
Maybe I've missed it, but that only seems to deal with
anyway, can't that task have that persistent buffer instead of having it in each task? Maybe I'm misunderstanding what @vtjnash meant by that though, I'm not really deeply familiar with how julia does IO internally. I know that a lot of stuff goes through libuv (does this have its own IO task? How does that work, is there some documentation I could read up on for this?), while other stuff (file IO, as I understand it?) goes through ios.c. |
Seems like I'm cursed to run into this again - encountered the fallback method while trying to make my |
Called in a tight loop, this quickly racks up allocations, overwhelming GC. This seems to be caused by the
Int
getting wrapped in aRef
further down the call chain, forcing it to be moved to the heap because of a@no_inline
even further down the call chain.Feels like primitve types in
write
shouldn't allocate.The text was updated successfully, but these errors were encountered: