Subject: Re: [ruby-ffi] FFI wiki code review request |
From: Wayne Meissner |
Date: 12/1/09 5:37 PM |
To: ruby-ffi@googlegroups.com |
2009/12/2 Jon <jon.forums@gmail.com>:
KEY QUESTIONS ============= 1) It appears all FFI objects created in Ruby code live in the heap. Is this correct? If yes, I've tried to highlight this in the C code by use of calloc/free. 2) There's no way to create FFI objects on the stack?
There is no concept of a C stack in FFI, so yes, everything lives on the heap - either the C heap (MemoryPointer, other Pointer instances), or the ruby heap (everything else).
3) In the "Enumerate Top Level Windows" example, should I use an FFI::MemoryPointer or an FFI::Buffer with :buffer_out in the :get_window_text signature? Both work in this specific example. The underyling question really is, "When should I use an FFI::MemoryPointer and when should I use an FFI::Buffer?"
Short answer: use MemoryPointer unless you're optimizing, and know what you're doing. Long answer: On some ruby VMs (such as jvm, clr), which use a copying/moving GC, there are two types of heap memory - the ruby heap and the native heap. The ruby heap is managed by the GC, and is very fast to allocate, access (get/put data) from ruby code, and release. To pass data from the ruby heap to/from native code, the data must be copied. The native heap is slower to allocate, slower to access from ruby code, and has significant management overhead for the VM. Since the memory is already on the native heap, it can be passed directly to native code without any additional copying. The heuristic on which one to use, is when the data is small and of a fixed size (e.g. a reference to an int that will be filled in by native code), use a Buffer, since the cost of the copy is small. For these small allocations, the MemoryPointer management overhead massively exceeds the copy cost. Simplified example: void get_path(char* path, int* lenp) { strlcpy(path, "foo", *lenp); *lenp = 3; } To call the above in C, you would do something like: char buf[512]; int len = sizeof(buf); get_path(buf, &len); To call it from FFI, you since the lenp param is small and fixed size, you could use a Buffer. module Foo attach_function :get_path, [ :buffer_out, :buffer_inout ], :void end # Use MemoryPointer for the path buf, since it is large-ish pathbuf = MemoryPointer.new :char, 512 # Use Buffer for the int reference, since it is small and fixed size lenp = Buffer.new :int Foo.get_path(pathbuf, lenp) # read out only len bytes as the path len = lenp.get_int(0) path = pathbuf.get_bytes(0, len) # lessen the load on the GC by freeing the path memory when no longer needed pathbuf.free
4) In the "Enumerate Top Level Windows" example, should I zero out the title buffer via "title.clear" similar to the C memset?
Yes. MemoryPointer and Buffer by default zero out the contents when allocated, but if you re-use them you will need to call clear.