From edfd75af8d47a3b3bbe43a514f791eb688d4fc7b Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 1 Jul 2024 11:06:56 +0100 Subject: [PATCH] Optimisation (#127) --- README.md | 6 ++++++ pyray/__init__.py | 45 ++++++++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 0f445c0..45ac2a0 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,10 @@ and then only convert them to C data structures when you have to call the C func 3. The raylib.* functions are potentially 1.5x faster than the pyray.* equivalents, so if you need a tiny bit more performance you can switch your inner loop functions to these. +4. There is a version of Python that is faster than Pypy: GraalPy. However it's not fully compatible with all Python +packages. It doesn't work with CFFI and so doesn't work with this binding. But it *is* compatible with the +*Java* binding, Jaylib! There is an example of this here: https://github.com/electronstudio/megabunny/tree/master/raylib-python-jaylib + ## Bunnymark @@ -175,6 +179,8 @@ you can switch your inner loop functions to these. | Raylib Python CFFI 3.7 | Python 3.9 Nuitka | 8600 | 5.1% | | Raylib Python CFFI 3.7 Dynamic | Python 3.9 | 6300 | 3.7% | +See also https://github.com/electronstudio/megabunny/ + # Packaging your app You can create a standalone binary using the Nuitka compiler. For example, here is how to package Bunnymark: diff --git a/pyray/__init__.py b/pyray/__init__.py index 705a102..289568d 100644 --- a/pyray/__init__.py +++ b/pyray/__init__.py @@ -51,35 +51,46 @@ def pointer(struct): # Another way to improve performance might be to special-case simple types before doing the string comparisons def _wrap_function(original_func): + c_args = [str(x) for x in ffi.typeof(original_func).args] + number_of_args=len(c_args) + c_arg_is_pointer = [x.kind == 'pointer' for x in ffi.typeof(original_func).args] + c_arg_is_string = [str(x) == "" for x in ffi.typeof(original_func).args] # print("makefunc ",a, ffi.typeof(a).args) def wrapped_func(*args): - modified_args = [] - for (c_arg, arg) in zip(ffi.typeof(original_func).args, args): - # print("arg:",str(arg), "c_arg.kind:", c_arg.kind, "c_arg:", c_arg, "type(arg):",str(type(arg))) - if c_arg.kind == 'pointer': - if type(arg) is str: - arg = arg.encode('utf-8') - # if c_arg is a 'char *' not a 'const char *' then we ought to raise here because its an out - # parameter and user should supply a ctype pointer, but cffi cant detect const + args=list(args) # tuple is immutable, converting it to mutable list is faster than constructing new list! + for i in range(0, number_of_args): + try: + arg=args[i] + except IndexError: + raise RuntimeError(f"function requires {number_of_args} arguments but you supplied {len(args)}") + if c_arg_is_pointer[i]: + if c_arg_is_string[i]: # we assume c_arg is 'const char *' + try: # if it's a 'char *' then user should be supplying a ctype pointer, not a Python string + args[i] = arg.encode('utf-8') # in that case this conversion will fail + except AttributeError: # but those functions are uncommon, so quicker on average to try the conversion + pass # and ignore the exception + # if user supplied a Python string but c_arg is a 'char *' not a 'const char *' then we ought to raise + # exception because its an out + # parameter and user should supply a ctype pointer, but we cant because cffi cant detect 'const' # so we would have to get the info from raylib.json - elif type(arg) is list and str(c_arg) == "": - arg = [ffi.new("char[]", x.encode('utf-8')) for x in arg] + elif c_args[i] == "" and type(arg) is list: + args[i] = [ffi.new("char[]", x.encode('utf-8')) for x in arg] elif is_cdata(arg) and "*" not in str(arg): - arg = ffi.addressof(arg) + args[i] = ffi.addressof(arg) elif arg is None: - arg = ffi.NULL + args[i] = ffi.NULL elif not is_cdata(arg): - if str(c_arg) == "": + if c_args[i] == "": raise TypeError( "Argument must be a ctype bool, please create one with: pyray.ffi.new('bool *', True)") - elif str(c_arg) == "": + elif c_args[i] == "": raise TypeError( "Argument must be a ctype int, please create one with: pyray.ffi.new('int *', 1)") - elif str(c_arg) == "": + elif c_args[i] == "": raise TypeError( "Argument must be a ctype float, please create one with: pyray.ffi.new('float *', 1.0)") - modified_args.append(arg) - result = original_func(*modified_args) + + result = original_func(*args) if result is None: return elif is_cdata(result) and str(result).startswith("