SRL Archive - Development

Simba(libmml) and Python.


Wizzup?

2010-09-12 15:24:14

Hiya,

I've done some work (and thinking) on Python and libmml(Simba core). This just keeps crossing my mind as we still don't have a (proper) interpreter for Simba. RUTIS and Pascalscript I would classify as OK, but not good. Python is an awesome language and has a very mature / featureful interpreter.

Working with primitive types is trivial, so I'll skip it and go straight to arrays.
There are very important as that's where all the CPU time / processing / speed is spent. (Eg, we don't need Wizzyplugin-power to get the current mouse position, but we do need it when we sort > 50k points.)

Here's how I propose we do it:

  • Python calls the function libMML->FindColors.
  • libMML->FindColors copies the TPA returned by the MML to memory. (This means no managed array issues. This involves one copy of the entire array.)
  • The copied data is passed to Python. Python can directly access the array data using ctypes. I'll refer to this in Python as PascalArray from now on.

Alternative ``copy'':

  • Python can directly access the array data and uses this data to create a list of the data and once the list is created it frees the array data. This can take up to 0.5s for 30k points. (VERY SLOW)

And if we want to pass an array to libMML from Python, we'd also do a copy. This probably isn't too much of an issue if we don't pass PascalArrays that are too huge. The only problem would be if we had to pass a python list. Python lists are slow compared to simple PascalArrays, and we'll have to turn that list into an Array again. Ultimately,both are possible, but speedwise the PascalArray is very much preferred.

This means that functions like getColors should accept both lists and PascalArrays. Another issue would be all the sorting functions. My suggestion is to simply incorporate these in the PascalArray class (or some class that extends it). This would ultimately provide proper speed (comparable to PascalScript with WizzyPlugin) and also make sure the sorting is accessible in an easy way.

So much for theory. Now a few examples. Please note that the code here is the ``under the hood'' code and is not representive for code in scripts. You could compare it to the code in Simba. (READ THIS PYTHON RANTERS)

My proposal:


def findAll(self, box, color, tol = 0):
"""
find all colors in a box, with a specific tolerance.
returned are all the matching points
"""
ptr, _len = PPOINT(), c_int(42)
if tol is 0:
self._mc.dll.find_colors(self._cli, byref(ptr), byref(_len),
color, *box)
else:
self._mc.dll.find_colors_tolerance(self._cli, byref(ptr),
byref(_len), color, tol, *box)

# Construct PascalArray
pa = PascalArray(ptr, _len)

# Do not free ptr!
return pa

Alt. version.


def findAll(self, box, color, tol = 0):
"""
find all colors in a box, with a specific tolerance.
returned are all the matching points
"""
ptr, _len = PPOINT(), c_int(42)
if tol is 0:
self._mc.dll.find_colors(self._cli, byref(ptr), byref(_len),
color, *box)
else:
self._mc.dll.find_colors_tolerance(self._cli, byref(ptr),
byref(_len), color, tol, *box)

# Construct python list
l = [(ptr[x].x, ptr[x].y) for x in range(_len.value)]

# Free ptr!
self._mc.free(ptr)

return l

This is code to the findColors(Tolerance) functions. It basically just does a dll call and return the results. It's that simple. One of them constructs a PascalArray class, the other one copies the data into a list and frees the data.


From here I will drop the alternative version in this post as I don't think it is a viable alternative.

A not-finished PascalArray class: (I didn't even test it)


class PascalArray(object):
"""
PascalArray is a class that allows one to easily access FPC/Pascal
memory in an array like way.

This class is a solution to passing arrays between libMML and Python.
The actual user of pymml should not be bothered by the external memory,
so as soon as there is no reference to this class anymore, Python calls
__del__ and we can safely free the memory.
"""
def __init__(self, pastype, ptr, MC, len):
"""
Set the type of the data we are holding to _pastype_,
save the pointer _ptr_ and store the reference to the MMLCore.
"""
self._type = pastype
self._p = ptr
self._mc = MC
self._len = len

def __del__(self):
"""
Free the array using FPC's FreeMem().
This is required because the memory was allocated using
FPC's AllocMem().
"""
self._mc.dll.fpc_freemem_(self._p)

def __len__(self):
"""
Return the length of the array.
"""
return self._len

def __getitem__(self, pos):
"""
Get an item at a specific position _pos_.
"""
if pos > len(self):
raise MMLException('Out of range')

return cast(self._p, POINTER(self._type))[pos]

def __setitem__(self, pos, item):
"""
Set an item at a specific position _pos_.
"""
if pos > len(self):
raise MMLException('Out of range')

if sizeof(item) != sizeof(self._type):
raise MMLException('Typesize does not match')

cast(self._p, POINTER(self._type))[pos] = item
return True

def resize(self, newsize):
"""
XXX: TODO
"""

So obviously as soon as Python notices the array class should be deleted as it is no longer used, __del__ is called, and the memory will be freed. So there shouldn't be much memory issues, as long as we don't mix memory with FPC. It can also resize the array, thus providing a SetLength() like Pascal.

A few things are still missing. Loading from a list; exporting itself as a list; and an implementation on how to pass data to libMML/FPC. This probably isn't too hard, but I'll mention it anyway as it isn't incorporated yet in the example.

Sorting this array will probably be done with a class that extends PascalArray. It will probably incorporate most (or all) Wizzyplugin sorting functions.

Called something like this:


tpa = Color.findAll((0, 0, 200, 200), 255, tol=25)
tpa.sortFrom((20, 20)

tpas = tpa.split(dist=20)

Alt:


tpa = Color.findAll((0, 0, 200, 200), 255, tol=25)
WizzyPlugin.SortTPAFrom(tpa)
tpas = WizzyPlugin.SplitTPA(tpa)

That's probably the most important part. If this works, everything else is trivial.
What do you guys think? Am I missing something?

This would probably make the Pascal -> Python converter worth trying, mopar.

E: I totally forgot to mention that this PascalArray can be used like normal arrays:


tpa = findAll((0, 0, 100, 100, clWhite)
print tpa[0].x
tpa[2].x = tpa[2].x - 10
# etc...

Wizzup?

2010-09-12 16:29:29

I will need to test if Python lists are actually that slow, or that it may also be ctypes that is slow. If that is the case, we have a problem because then we can never make custom sorting very fast.

E: Fortunately it seems that just iterating over all the points is a lot faster than creating a list of them. So the bottleneck is not ctypes. :D

Nava2

2010-09-12 16:32:51

I love the TPA object, and with the properties and extensions of lists, it makes it even nicer.

Python is win!

Could we get some documentation on the conversion between python and C (FPC) so we can help to make the ``under the hood'' functions quicker coming?

From: Wizzup?
I will need to test if Python lists are actually that slow, or that it may also be ctypes that is slow. If that is the case, we have a problem because then we can never make custom sorting very fast.


What we could do is implement sorting algorithms in straight C rather than FPC, would make them as fast as possible.

Kinda forgot about the fact that with python there is a lot more flexibility as far as extending goes.

Wizzup?

2010-09-12 16:37:04

From: Nava2
What we could do is implement sorting algorithms in straight C rather than FPC, would make them as fast as possible.


Nah, the problem would have been that ctypes (what I use in python to communicate with C/FPC) is slow. But fortunately, it isn't really slow. See my edit. :)

E: Yeah. I can probably write some doc. Does anyone ever use them though? :p

Nava2

2010-09-12 16:46:26

From: Wizzup?
Nah, the problem would have been that ctypes (what I use in python to communicate with C/FPC) is slow. But fortunately, it isn't really slow. See my edit. :)

E: Yeah. I can probably write some doc. Does anyone ever use them though? :p


Yeah I would! I've been wanted to work at this for awhile. But have been missing the necessary docs. :(

moparisthebest

2010-09-12 20:31:57

Honestly, half a second for 30,000 point array doesn't seem that bad to me, how often do arrays of that size get used at all in this context anyway? (I can't imagine it would be very often, I could be wrong).

Anyway, the PascalArray class still seems like the best bet, as you can still convert to a python list if you want/need to. Out of curiousity have you checked how fast it is to iterate over a python list as opposed to a PascalArray? It's possible it could be much slower, that actually could have caused the original slowdown in the first place.

From: Wizzup?

This would probably make the Pascal -> Python converter worth trying, mopar.


Yea it would! :stirthepot:

I'm just waiting for you to embed the python interpreter into Simba and I'll work on whipping up a converter. :) (hopefully I'll be done building my barn by then…)

Nava2

2010-09-12 22:55:09

From: moparisthebest
Yea it would! :stirthepot:

I'm just waiting for you to embed the python interpreter into Simba and I'll work on whipping up a converter. :) (hopefully I'll be done building my barn by then…)


No need to wait, you can run things from the terminal just the same!

Dan Cardin

2010-09-13 07:04:01

I dont see what the point of all this would be if you can just get a pascal -> python converter. It would make dealing with FPC at all obsolete, no?

No need to wait, you can run things from the terminal just the same!
i agree. Waiting is for sillies!

nielsie95

2010-09-13 10:39:15

From: Dan Cardin
I dont see what the point of all this would be if you can just get a pascal -> python converter. It would make dealing with FPC at all obsolete, no?


You still need to call functions like FindColors from Python.

Wizzup?

2010-09-13 13:13:55

From: moparisthebest
Honestly, half a second for 30,000 point array doesn't seem that bad to me, how often do arrays of that size get used at all in this context anyway? (I can't imagine it would be very often, I could be wrong).

Anyway, the PascalArray class still seems like the best bet, as you can still convert to a python list if you want/need to. Out of curiousity have you checked how fast it is to iterate over a python list as opposed to a PascalArray? It's possible it could be much slower, that actually could have caused the original slowdown in the first place.



Yea it would! :stirthepot:

I'm just waiting for you to embed the python interpreter into Simba and I'll work on whipping up a converter. :) (hopefully I'll be done building my barn by then…)



Hm, 30k points is about the full size of the client, I think. IMO it is simply too slow; but even it would twice as fast, there would still be a problem with the point sorting. (This needs to be fast as well)

I didn't check how fast it is to iterate over a list vs PascalArray, but I can only hope the difference won't be that big. Probably won't be much of an issue though.

From: Nava2
No need to wait, you can run things from the terminal just the same!


It would probably be useful if at least libmml works. ;)

moparisthebest

2010-09-13 15:44:41

From: Nava2
No need to wait, you can run things from the terminal just the same!


But I don't know what all the function names are going to be renamed to in python and the like. True I could get started, but I'm to busy now anyway what with me building a barn and all. Another month or so and I should be done! :)

From: Wizzup?
Hm, 30k points is about the full size of the client, I think. IMO it is simply too slow; but even it would twice as fast, there would still be a problem with the point sorting. (This needs to be fast as well)

I didn't check how fast it is to iterate over a list vs PascalArray, but I can only hope the difference won't be that big. Probably won't be much of an issue though.


That's about what I was thinking, so it will NEVER return anywhere near 30k points (unless the client screen is all one color, that never happens does it?), which means it will be far less than half a second every time. Sorting once it is already a python list should be very fast, I don't see the problem with that.

I'd check the speed pretty soon, because if it's significantly slower using a PascalArray it would be better and easier to just take the time hit once in the beginning to convert to a list. (obviously if it's just as fast, or faster, then it's better to stick with PascalArray)

Nava2

2010-09-13 16:06:29

Why don't we subclass list and override the parts that we need. It would make it so that we can use the arrays we have with other libraries.

Wizzup?

2010-09-13 17:18:36

From: moparisthebest
That's about what I was thinking, so it will NEVER return anywhere near 30k points (unless the client screen is all one color, that never happens does it?), which means it will be far less than half a second every time. Sorting once it is already a python list should be very fast, I don't see the problem with that.


Well, it is a real bottleneck is you for example, want to loop over x amount of points until you find the correct one. I don't want to be a speed freak; but 0.5 is a lot considering the actual search takes ~15ms. So 485ms is spent just to turn it into a python list. (Remember that sometimes intensive scripts call it about 10-15 times a second, even though the area will probably be about 30x30 in that case.) Sure, if you intend to use it a lot, it may be worth turning it into a list. We could probably just add that will either turn it to a list or PascalArray. But for now performance wise it really seems like a good idea to use PascalArray. I know lists can be quite fast, but tbh it's still slow compared to the functions we have in tpa.pas/Wizzyplugin. (Even not all the stuff in Wizzyplugin is really optimised, but at least it doesn't have to do on the fly type checking, etc. ;)

From: moparisthebest
I'd check the speed pretty soon, because if it's significantly slower using a PascalArray it would be better and easier to just take the time hit once in the beginning to convert to a list. (obviously if it's just as fast, or faster, then it's better to stick with PascalArray)


I'd like to hear about the speed. If you provide me with a script I can use it to benchmark PascalArray as well. (Considering I didn't yet commit my latest code, you may have some issues with testing it)

moparisthebest

2010-09-13 18:30:01

From: Wizzup?
Well, it is a real bottleneck is you for example, want to loop over x amount of points until you find the correct one. I don't want to be a speed freak; but 0.5 is a lot considering the actual search takes ~15ms. So 485ms is spent just to turn it into a python list. (Remember that sometimes intensive scripts call it about 10-15 times a second, even though the area will probably be about 30x30 in that case.) Sure, if you intend to use it a lot, it may be worth turning it into a list. We could probably just add that will either turn it to a list or PascalArray. But for now performance wise it really seems like a good idea to use PascalArray. I know lists can be quite fast, but tbh it's still slow compared to the functions we have in tpa.pas/Wizzyplugin. (Even not all the stuff in Wizzyplugin is really optimised, but at least it doesn't have to do on the fly type checking, etc. ;)



I'd like to hear about the speed. If you provide me with a script I can use it to benchmark PascalArray as well. (Considering I didn't yet commit my latest code, you may have some issues with testing it)


I agree with you it's probably best to use PascalArray, especially because if the programmer wants it in a python list it can always be converted whenever they want to do it (it should have a .toList() function eventually).

I'm not talking about very scientific tests, maybe just construct a list and a PascalArray with the same numbers, pretty long (30k points if you want) and then time how long it takes to do common things on them. Search for a particular number, sort it using various sorting methods (bubblesort, quicksort at least, etc). That's all I'd do anyway.

Markus

2010-09-14 12:25:28

From: Wizzup?
Hm, 30k points is about the full size of the client, I think. IMO it is simply too slow; but even it would twice as fast, there would still be a problem with the point sorting. (This needs to be fast as well)


client = ±760*500 = 380k, so 30k points (< 10% off the screen) is more common than we may actually think. The mainscreen alone is 542*542 = 294k points. One rock can easily fit that.
(Just stating that we DO need to care about performance issues, after all, we all remember the times before wizzyplugin don't we? bankfinding was slow as F!$#)