Good night list !!
some reading, for insomniacs, and 3 questions : I did read carefully DLL & C Connect User's Guide S I M P L I F I C A T I O N T H R O U G H I N N O V A T I O N ® VisualWorks 7.9 P46-0112-10
I have to do interfaces with DLL implying callbacks, and dealing with large amount of floating points values organised into arrays. The callback running time is critical, so I try to minimize the time exchanging data between C and Smalltalk environment. I found in the OpenGL package, the class FloatArray, which, at first look, seemed to be helpful in my situation : I can pass, directly an instance of OpenGL.FloatArray as a float * argument of a C function, example (from the OpenGL package )
OpenGL.FloatArray setUniform: index gl Uniform1fv: index with: self size with: self. OpenGL.gl CheckError
Uniform1fv: location with: count with: value <C: void glUniform1fv(GLint location, GLsizei count, const GLfloat * value)> ^self externalAccessFailedWith: _errorCode
The const GLfloat * value « is » a FloatArray.
It is working, but I have some doubt : There is a strange definition in class FloatArray :
FloatArray class newInFixedSpace: anInteger
^super basicNew: anInteger * 4
Why the #newInFixedSpace: has been redefined ?
I try some test allocating the FloatArray in the heap by calling gcCopyToHeap :
| x xPtr | self halt. x := OpenGL.FloatArray new: 150. 1 to: 150 do: [:index | x at: index put: index ]. xPtr := x gcCopyToHeap.
And using the debugger I can see that the resulting malloc allocates 4 times he memory needed. (copyToHeap: #malloc) inherited by IntegerArray is not appropriate for FloatArray(s). as it is clearly stated in the doc :
« The heap copy protocol cannot copy an arbitrary Smalltalk object (or object graph) to the heap. It is designed to copy scalar objects (numbers, strings, and byte arrays). The classes whose instances can be copied directly with this protocol are as follows: Integer, Double, Float, Character, ByteArray, ByteEncodedString (and its subclasses), IntegerArray, TwoByteString, UninterpretedBytes, and WordArray. »
« You must override the implementation of copyToHeap if you want to write specialized code to copy instances of one of your application classes to the heap. »
I did some suplementary tests :
I create a simple dll, with a function that returns the address we pass to it :
.h const float * FloatIdentite(const float *source);
.c const float * DLL_EXPORT FloatIdentite(const float *source) { return source; }
And call it from Smalltalk :
| x itf ptr1 ptr2 | x := OpenGL.FloatArray new: 900000.
itf := self new. "self is the ExternalInterface class" ptr1 := itf FloatIdentite: x.
ObjectMemory globalGarbageCollect.
ptr2 := itf FloatIdentite: x.
^ptr1 -> ptr2
"a CPointer {FF610028} (const float * ) -> a CPointer {FCB50028} (const float * )"
The address of the FloatArray x as moved between the two calls (because the FloatArray is not in the FixedSpace).
So, I try to allocate the FloatArray in the FixedSpace :
| x itf ptr1 ptr2 | x := OpenGL.FloatArray newInFixedSpace: 900000.
itf := self new. ptr1 := itf FloatIdentite: x.
ObjectMemory globalGarbageCollect. ptr2 := itf FloatIdentite: x.
^ptr1 -> ptr2 "a CPointer {FE2314F4} (const float * ) -> a CPointer {FCC3A6FC} (const float * )"
The result is the same because of the redefinition of #newInFixedSpace: The same problem arise when I send an #asFixedArgument message.
| x | x := OpenGL.FloatArray newInFixedSpace: 900000. x asFixedArgument isFixedArgument
→ false !!!
The first question is : Is there a way to allocate FloatArray(s) in FixedSpace ? or why not ?
Actually, I use a gcMalloc: and the « copyAt: offset from: byteLikeObject size: count startingAt: startIndex » to transfer bytes from the « moving » FloatArray to a fixed float array in the heap.
| x t1 gcPtr t2 z t3 | "1/ création d'un tableau de flottants et affectation des valeurs" x := OpenGL.FloatArray new: 900000.
t1 := Time microsecondsToRun: [ 1 to: 900000 do: [:index | x at: index put: index ]].
"2/ allocation des octets dans le tas" gcPtr := CLimitedPrecisionRealType float gcMalloc: 900000.
"3/ transfert" t2 := Time microsecondsToRun: [ gcPtr copyAt: 0 from: x size: 900000 * 4 startingAt: 1 ].
"4/ création d'un nouveau tableau de flottants Smalltalk et transfert en sens inverse." z := OpenGL.FloatArray new: 900000. t3 := Time microsecondsToRun: [ gcPtr copyAt: 0 to: z size: 900000 * 4 startingAt: 1 ].
^ (Array "with: x with: z" with: t1 with: t2 with: t3)
"→ #(71588 1263 513)"
My second question is :
Am I obliged to copy my float arrays before passing them to the C functions ? If I get the address of a FloatArray with the « FloatIdentite » function, at any time between two Smalltalk instructions a GC can occur, and move the table elsewhere. Is there a mean to « freeze » the GC, while doing a critical opération ?
Third point (subsidiary question). The increment method for CPointer(s) is very slow ; Incrémenter un pointeur en langage C est juste une seule instruction en langage d'assemblage. Incremeting a CPointer in Smalltalk invokes : #+= 1
| floatPtr | self halt. floatPtr := CLimitedPrecisionRealType float gcMalloc: 1. floatPtr += 1.
CPointer += offset "Increment the receiver by offset elements. The receiver is answered with its referent address properly adjusted. The pointer value will be adjusted by (offset * size of the receiver's referent type). Please see the class comments for more details."
theDatum := theDatum + (type referentType dataSize * offset)
CPointerType referentType "Answer the type the receiver references."
^referentType
CScalarType dataSize "Answer the number of bytes occupied by a type instance of the receiver."
^numBits + 7 bitShift: -3
The gain of time if we assume the a float is 4 bytes and we redefine a special method to increment the pointer is about 11% :
incrementSpecial theDatum := theDatum + 4
Clearly, a loop with a CPointer and increment message in not the good way to speed up the copy process.
Question : why this implementation of #copyToHeap: in IntegerArray ? Is it just to check one by one, if the integer value fits into the C interger limits ?
copyToHeap: mallocSelector "Copy the receiver to the external heap. The argument is a method selector that accepts one argument and determines how to allocate data for the receiver -- it is typically one of #malloc: or #malloc16:. The selector's argument is the number of objects of the receiver's baseCType to allocate. Answer a pointer to the data. If the allocation fails a primitive failed signal is raised."
| aPointer currentPointer size | aPointer := self baseCType referentType perform: mallocSelector with: self basicSize. currentPointer := aPointer copy. size := self size. 1 to: size do: [:index | currentPointer contents: (self at: index). currentPointer += 1]. ^aPointer
Thanks for any suggestion. Vincent _______________________________________________ vwnc mailing list [hidden email] http://lists.cs.uiuc.edu/mailman/listinfo/vwnc |
Free forum by Nabble | Edit this page |