-
-
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
RFC: mmap makeover #11280
RFC: mmap makeover #11280
Conversation
👍 My main question is just whether |
end | ||
|
||
# Mmapped-bitarray constructor | ||
function mmap_bitarray{N}(dims::NTuple{N,Integer}, s::IOStream, offset::FileOffset) | ||
mmap_array{N}(::Type{Bool}, dims::NTuple{N,Integer}, s::IOStream, offset::FileOffset) = | ||
mmap_bitarray(dims, s, offset) |
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 will cause trouble for anyone who is actually wanting to mmap an array of bools, i.e., UInt8 with values of 0 or 1.
I also wonder if we should add a convenience function |
Yes, good point on the Yes, I also had the idea of adding the convenience functions. From what I understand, we should be clear on Windows, since it uses a separate |
Anyone know what's up with the |
@twadleigh, @amitmurthy, @carlobaldassi. You three seem to be the local sharedarray.jl experts. Any idea why the changes here are causing SharedArray to segfault on unix? (it seems that any call to I tried to not touch the actual implementation details of mmap at all and just copy/paste code around, but in my initial debugging efforts, I haven't uncovered what the cause here is. |
When I implemented the Windows version of I think defining a consistent mmap interface across platforms is (1) a worthy goal, and (2) overdue. Even better would be if the resulting interface admitted not-entirely-unnatural implementations on each platform. I don't have any easy answers for you right now, but I'll devote some time to pondering this issue this weekend. |
In retrospect I was too conservative in my reviews on those PR's, we were much earlier in the 0.3.x series at the time and making sure things could be made safe to backport was a big concern for me. If we can get this settled in the next few weeks (total guess on timeline btw, don't take anything I say too seriously) before soft-freezing 0.4-pre, it would be good to brainstorm a better, more elegant API across all platforms. I'm not too familiar with the low-level subtleties here, but you have my moral support for being a bit aggressive with this. |
Does |
It does indeed @timholy; julia> A = SharedArray(Float32, (5,5))
right before calling init function
Float32[
signal (11): Segmentation fault: 11
anonymous at sharedarray.jl:219
jl_apply at /Users/jacobquinn/julia/src/gf.c:1750
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
isassigned at abstractarray.jl:89
print_matrix_repr at show.jl:1158
showarray at show.jl:1196
jlcall___showarray#22___113606 at (unknown line)
jl_apply at /Users/jacobquinn/julia/src/gf.c:1750
julia_showarray_113604 at (unknown line)
showall at show.jl:1229
jl_apply at /Users/jacobquinn/julia/src/gf.c:1750
SharedArray at sharedarray.jl:97
julia_call_113513 at (unknown line)
jlcall_call_113513 at (unknown line)
jl_apply at /Users/jacobquinn/julia/src/gf.c:1750
jl_apply at /Users/jacobquinn/julia/src/interpreter.c:55
eval at /Users/jacobquinn/julia/src/interpreter.c:212
eval at /Users/jacobquinn/julia/src/interpreter.c:218
eval_body at /Users/jacobquinn/julia/src/interpreter.c:579
jl_toplevel_eval_body at /Users/jacobquinn/julia/src/interpreter.c:514
jl_toplevel_eval_flex at /Users/jacobquinn/julia/src/toplevel.c:464
jl_eh_restore_state at /Users/jacobquinn/julia/src/./julia.h:1378
eval_user_input at REPL.jl:62
jlcall_eval_user_input_113505 at (unknown line)
jl_apply at /Users/jacobquinn/julia/src/gf.c:1750
anonymous at task.jl:91
jl_apply at /Users/jacobquinn/julia/src/task.c:234
Segmentation fault: 11
jq-mbp:julia jacobquinn$ Can you give me a tip or two on debugging this? I'm happy to dive in, I just feel like a little direction would probably go along way. Thanks! |
First do simple stuff like |
FYI, @quinnj, I'm starting to take a swing right now at a possible refactoring of |
Ok, I think I've managed to tame the
There is now a single method definition for all platforms for each of the above functions (which means callees don't have to differentiate anymore), and that actually ended up consolidating a lot of code. Minus the 107 lines of additional tests, this is a net decrease of ~20 LOC for implementation. Nifty! I think I've got the deprecations correct, but I should probably stress test them a little more. I also need to do a doc update, but otherwise, I'd appreciate further feedback on the design/overhaul. (oh, and the segfault I was seeing earlier has been resolved too. All tests now pass for me locally on both windows 64-bit and OSX Yosemite 64-bit). |
There was a 32 bit Linux Travis failure on the push build but not the PR build, but it was just a spurious connection problem downloading Rmath-julia so I restarted it. |
I only skimmed through the changes but from the summary I like this. (As an aside: I wouldn't call myself a sharedarray expert in any way, I made some very limited contributions to those.) |
ptr::Ptr{Void} # pointer to mmapped-memory | ||
handle::Ptr{Void} # only needed on windows for file mapping object | ||
len::Int # amount of memory mapped | ||
offset::Int |
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 someone on 32bit wants access to the range 10-12GB in a large file, does making this an Int
mean they are out of luck? Make it a FileOffset instead?
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.
Yes... as somebody who did a lot of accessing files in the TB range, on 32-bit machines, that really does need to be something bigger than a 32-bit int!
Aside from one small but important point, see nothing but goodness here. Nice work! |
@carlobaldassi, I'm doing another pass over the deprecations and remembered that I took out the following: mmap_bitarray{N}(::Type{Bool}, dims::NTuple{N,Integer}, s::IOStream, offset::FileOffset) =
mmap_bitarray(dims, s, offset)
mmap_bitarray{N}(::Type{Bool}, dims::NTuple{N,Integer}, s::IOStream) = mmap_bitarray(dims, s, position(s)) Any reason to keep something similar for the new |
@quinnj: those definitions were there only for consistency with the |
👍 💯 This (and the other segment violations you've been finding) I think will be very useful (and much safer!) |
Hmmmm.......I feel like the The output of running gdb is below, but the several different backtraces aren't really speaking to me. I'm not sure if this is a system thing or what necessarily; if anyone feels really kind and would help take a look, I'd really appreciate it. Even just by shedding some light (if any) on the gdb backtraces. Thanks. |
I think I've made some progress? I've boiled it down to an interesting case. A stand-alone module you can run is here, and the script to produce the segfault is here. What's interesting is that running under It's fun getting into the gdb world of things, but honestly it's not my forte. Anyone feel up for helping debug this? |
One more update (sorry to be so noisy on this). Turns out it doesn't segfault in signal (11): Segmentation fault: 11
arraylist_pop at /Users/jacobquinn/julia/src/support/arraylist.c:72
run_finalizers at /Users/jacobquinn/julia/src/gc.c:129
jl_gc_collect at /Users/jacobquinn/julia/src/gc.c:2349
__pool_alloc at /Users/jacobquinn/julia/src/gc.c:1036
pool_alloc at /Users/jacobquinn/julia/src/gc.c:1086
allocobj at /Users/jacobquinn/julia/src/gc.c:2427
_new_array_ at /Users/jacobquinn/julia/src/array.c:84
_new_array at /Users/jacobquinn/julia/src/array.c:139
jl_alloc_array_1d at /Users/jacobquinn/julia/src/array.c:322
unlock at ./lock.jl:34
println at stream.jl:230
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
jl_apply_generic at /Users/jacobquinn/julia/src/gf.c:1743
seg at none:10
jlcall_seg_44559 at (unknown line)
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
jl_trampoline at /Users/jacobquinn/julia/src/builtins.c:962
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
jl_apply_generic at /Users/jacobquinn/julia/src/gf.c:1767
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
do_call at /Users/jacobquinn/julia/src/interpreter.c:65
eval at /Users/jacobquinn/julia/src/interpreter.c:212
eval at /Users/jacobquinn/julia/src/interpreter.c:218
eval_body at /Users/jacobquinn/julia/src/interpreter.c:572
jl_interpret_toplevel_thunk_with at /Users/jacobquinn/julia/src/interpreter.c:610
jl_interpret_toplevel_thunk at /Users/jacobquinn/julia/src/interpreter.c:617
jl_toplevel_eval_flex at /Users/jacobquinn/julia/src/toplevel.c:522
jl_toplevel_eval at /Users/jacobquinn/julia/src/toplevel.c:530
jl_toplevel_eval_in at /Users/jacobquinn/julia/src/builtins.c:539
jl_f_top_eval at /Users/jacobquinn/julia/src/builtins.c:568
eval_user_input at REPL.jl:62
jlcall_eval_user_input_44514 at (unknown line)
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
jl_apply_generic at /Users/jacobquinn/julia/src/gf.c:1743
anonymous at task.jl:91
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
jl_trampoline at /Users/jacobquinn/julia/src/builtins.c:962
jl_apply at /Users/jacobquinn/julia/src/./julia.h:1290
start_task at /Users/jacobquinn/julia/src/task.c:233
Segmentation fault: 11 I'm not sure what |
You're calling If you want to make it safe to call Finally, I take back what I said about not wrapping it, of course you're right that just delegating all indexing operations to an internal array is fine and an easy way to get full functionality. |
Interesting. So is that a bug @timholy that I'm seeing a segfault in this case? I mean, I can manually create an In any case, I'm already including |
Not sure. If you run |
So, one problem is that if you access an mmapped array in a loop that modifies memory, you will probably have to load both I think it would be good to leave the Libc functions. They aren't really any more obsolete now than they were when we had |
@simonster, you could access the underlying array I just pushed another big iteration that solidifies the array interface for the new |
You can still find the past build results via navigating on Travis and AppVeyor. I wouldn't have put [ci skip] on the squashed final commit personally, unless the CI queue was a lot worse than it is right now. |
@carlobaldassi, here's a question I think I already know the answer to: when we do file = tempname()
open(file,"w") do f
write(f,UInt64(1))
write(f,UInt8(1))
end
m = Mmap.mmap(file, BitArray) Should return a 72-element BitArray instead of 9-element, right? I guess we'd want to be consistent with how we write BitArrays out to a file; do we just write the bit values out? |
Discussion here about the BitArray write format. |
@tkelman, any idea on the AppVeyor failures here? Things looked fine for me locally. |
sorry, screwed some things up by pushing straight to master like I keep telling other people not to do |
Alright, this is passing pretty much everywhere (the 64-bit AppVeyor timed out, but did run the file + mmap tests before timing out). I've updated the top post to reflect the current API/changes. The main update here is the use of I'd appreciate any comments and feedback. |
Bump. Any thoughts? If not, I'll merge in a day or two. |
CI all passed; merging. |
👏 👍 Very nice! |
According to http://pkg.julialang.org/pulse.html, seems like every package that uses mmap is timing out and being killed. Any ideas why this would cause such a regression? |
Drat, it looks like my deprecations got borked somehow to an older version; having to rebase the deprecations must have got tripped up. Ref #12070, which I'll merge when green. |
pos = position(io) | ||
filelen = filesize(io) | ||
if filelen < offset + len | ||
write(io, Base.zeros(UInt8,(offset + len) - filelen)) |
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 should probably go back to using pwrite
, since that only had to write a single byte, whereas this is allocating and writing a potentially large array of zeros. It also doesn't seem that this seeks to the end of the file first, so it doesn't seem like it would yield the correct behavior if the file already exists?
For those of us migrating over to the new syntax, it would be great to have |
For those wanting compat: JuliaLang/Compat.jl#123 |
[Updated to reflect latest changes]
This PR overhauls the overall mmap interface by the following changes:
Libc.mmap
interface: it was only implemented for unix, and didn't even match the fullmmap
functionality that's available (i.e. anonymous maps). The implementation was weirdly split betweenmmap_array
andmmap
, except for windows, where the entire implementation was inmmap_array
. The coremmap
code is now completely consolidated within the coreMmap.mmap
function, which represents the real, unifying interface that is cross-platform.mmap_array
withMmap.mmap
which returns a regularArray
whose contents point to a memory-mapped region. Several convenience constructors are added for working on file names directly.Mmap.mmap
takes an::Type{Array{T,N}}
as input determining the resultingArray
type to return; this is to provide consistency betweenMmap.mmap(io, Vector{UInt8}...)
andMmap.mmap(io, BitVector...)
SharedMemSpec
type, and this didn't exist for unix. Now, they are unified through theMmap.Anonymous
IO
type, more generally useable throughMmap.mmap{T}(::Type{Array{T,N}}, dims)
).offset
was specified tommap_array
Mmap.mmap
(e.g. check thatio
is open, that we have a sanelen
argument, that a file has write permissions if we need to grow it, thatArray{T,N}
is a bits type, etc.)munmap
method in favor of automatically finalizing an mmapped-Array upon construction; this removes the possibility of a user manually munmapping and then getting a segfault when trying to access a memory-mmaped arraymsync
withMmap.sync!
to flush changes made to a mmapped-Array back to disk+574/ -299 LOC diff mainly consists of:
The core implementation is actually smaller than before (~175 lines vs. ~235 lines), even with additional functionality.