
Hi, I do a lot of programming with small compound objects, things like 3d vectors or RGBA tuples for images. For performance reasons, I keep them in StorableArrays, since there is no way to use these things with UArrays. (But that's a topic for another thread.) Some how the notion got into my head that StorableArrays were slow, I think from old versions of GHC. So I've gotten into the habit of using withStorableArray and peek/poke to work on my arrays. This, as you can imagine, is cumbersome. Not only because of the extra typing, but also because I have to do everything in the IO monad, even if the array is read-only. So I'm thinking about writing my own IArray instance using ForeignPtr for storage and unsafePerformIO for access. I did a little experiment, comparing
forM_ [0..lots] $ \i -> withForeignPtr fp $ \p -> pokeElemOff p i i
to
withForeignPtr fp $ \p -> forM_ [0..lots] $ \i -> pokeElemOff p i i
And there seemed to be no difference in running time, which is great. So I'm thinking of making unsafeAt something like
unsafeAt (MyArray fp) i = unsafePerformIO $ withForeignPtr fp $ \p -> peekElemOff p i
My question is: what exactly does GHC.Prim.touch# do? This appears to be the critical function of withForeignPtr, and if I write my IArray instance as above, I have no way to float this touch# out of my loops, as I currently do using withStorableArray. It appears from my simple experiment that touch# does nothing at runtime. But maybe this is only because no memory is allocated inside my simple loop? Maybe if I write non-trivial loops, then touch# will actually cost something? Intensely curious, Scott

Hello Scott, Sunday, December 16, 2007, 11:57:33 PM, you wrote:
My question is: what exactly does GHC.Prim.touch# do? This appears to
it's a no-op (for ghc 6.6+ at least). its only task is to notify ghc optimizer that data were accessed so it doesn't free the memory -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Bulat Ziganshin wrote:
Hello Scott,
Sunday, December 16, 2007, 11:57:33 PM, you wrote:
My question is: what exactly does GHC.Prim.touch# do? This appears to
it's a no-op (for ghc 6.6+ at least). its only task is to notify ghc optimizer that data were accessed so it doesn't free the memory
Yes, exactly. touch# generates no code, but it is vitally important because if the ForeignPtr is referencing data in the heap (like mallocForeignPtrBytes does), it prevents the data from being GC'd before the operation completes. Cheers, Simon

Hello Simon, Monday, December 17, 2007, 1:33:19 PM, you wrote:
My question is: what exactly does GHC.Prim.touch# do? This appears to
it's a no-op (for ghc 6.6+ at least). its only task is to notify ghc optimizer that data were accessed so it doesn't free the memory
Yes, exactly. touch# generates no code, but it is vitally important because if the ForeignPtr is referencing data in the heap (like mallocForeignPtrBytes does), it prevents the data from being GC'd before the operation completes.
a bit more details for Scott: generated code is like this: ptr <- unsafeForeignPtrToPtr fptr yourAction ptr touch# fptr without touch, the *last* action where fptr involved is its conversion to ptr. GHC Runtime (not optimizer as i said) have no idea that ptr and fptr is the same object, so after conversion it feels free to dispose object pointed by fptr if GC occurs. this means that during execution of your action data pointed by fptr/ptr may be suddenly freed, allocated by other object, bang! the touch pseudo-action is performed *after* your action and references fptr again. so Runtime thinks "there is one more usage of fptr after yourAction" and it doesn't dispose this chunk of memory if GC occurs during your action (unfortunately it's not mentioned anywhere on the Web) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Thanks for the response guys.
I'm still curious as to the specific mechanism by which a strictly
compile-time function affects the run-time behavior of the GC. Bulat
says that touch# makes the runtime aware that the fptr object is still
in use, but how does it do this if, at runtime, it doesn't do
anything? Does if affect the scheduling of collections? Does it mark
the fptr object with some flag when its allocated? What if, for
example, if I put the touch# behind a branch that is conditional on
run-time values? How would this affect things?
I'm sure the answer to this is detailed and involves a lot of
specifics about GHC that I probably won't grasp, and maybe it isn't
appropriate for the whole list, but if there happens to be a short
answer, or there's a reference you can point me to, I'd appreciate it.
Thanks,
Scott
On Dec 17, 2007 4:12 AM, Bulat Ziganshin
Hello Simon,
Monday, December 17, 2007, 1:33:19 PM, you wrote:
My question is: what exactly does GHC.Prim.touch# do? This appears to
it's a no-op (for ghc 6.6+ at least). its only task is to notify ghc optimizer that data were accessed so it doesn't free the memory
Yes, exactly. touch# generates no code, but it is vitally important because if the ForeignPtr is referencing data in the heap (like mallocForeignPtrBytes does), it prevents the data from being GC'd before the operation completes.
a bit more details for Scott:
generated code is like this:
ptr <- unsafeForeignPtrToPtr fptr yourAction ptr touch# fptr
without touch, the *last* action where fptr involved is its conversion to ptr. GHC Runtime (not optimizer as i said) have no idea that ptr and fptr is the same object, so after conversion it feels free to dispose object pointed by fptr if GC occurs. this means that during execution of your action data pointed by fptr/ptr may be suddenly freed, allocated by other object, bang!
the touch pseudo-action is performed *after* your action and references fptr again. so Runtime thinks "there is one more usage of fptr after yourAction" and it doesn't dispose this chunk of memory if GC occurs during your action
(unfortunately it's not mentioned anywhere on the Web)
-- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Scott Dillard wrote:
Thanks for the response guys.
I'm still curious as to the specific mechanism by which a strictly compile-time function affects the run-time behavior of the GC. Bulat says that touch# makes the runtime aware that the fptr object is still in use, but how does it do this if, at runtime, it doesn't do anything? Does if affect the scheduling of collections? Does it mark the fptr object with some flag when its allocated? What if, for example, if I put the touch# behind a branch that is conditional on run-time values? How would this affect things?
It's not as complicated as you think. touch# takes a single argument, and does nothing with it; it's only purpose is to ensure that the value passed as its argument is "alive" as far as the rest of the compiler and runtime is concerned. There's no special mechanism at work here: touch# is just like an ordinary IO operation, except that its implementation is empty, and the compiler cannot use this fact for optimisation purposes. The compiler therefore arranges that the argument to touch# is available, just as it would for any other function. Cheers, Simon

On Mon, Dec 17, 2007 at 02:12:31PM +0300, Bulat Ziganshin wrote:
Hello Simon,
Monday, December 17, 2007, 1:33:19 PM, you wrote:
My question is: what exactly does GHC.Prim.touch# do? This appears to
it's a no-op (for ghc 6.6+ at least). its only task is to notify ghc optimizer that data were accessed so it doesn't free the memory
Yes, exactly. touch# generates no code, but it is vitally important because if the ForeignPtr is referencing data in the heap (like mallocForeignPtrBytes does), it prevents the data from being GC'd before the operation completes.
a bit more details for Scott:
generated code is like this:
ptr <- unsafeForeignPtrToPtr fptr yourAction ptr touch# fptr
without touch, the *last* action where fptr involved is its conversion to ptr. GHC Runtime (not optimizer as i said) have no idea that ptr and fptr is the same object, so after conversion it feels free to dispose object pointed by fptr if GC occurs. this means that during execution of your action data pointed by fptr/ptr may be suddenly freed, allocated by other object, bang!
I'd like to elaborate that ptr, as far as the GC is concerned is *not a pointer at all*, it is an integer memory address. So it doesn't keep anything alive, you need to hold on to fptr if you want the value to exist. Stefan
participants (4)
-
Bulat Ziganshin
-
Scott Dillard
-
Simon Marlow
-
Stefan O'Rear