tonybaloney / CSnakes

https://tonybaloney.github.io/CSnakes/
MIT License
291 stars 21 forks source link

preallocate based on items.Length and use PyList_SetItem #173

Closed github-actions[bot] closed 1 month ago

github-actions[bot] commented 2 months ago

https://github.com/tonybaloney/CSnakes/blob/4090d5b794d8cabec10d75f723673a026167d65f/src/CSnakes.Runtime/Python/Pack.cs#L42


using CSnakes.Runtime.CPython;
using System.Runtime.InteropServices.Marshalling;

namespace CSnakes.Runtime.Python;

/// <summary>
/// These methods are used internally to create a PyObject where the Dispose() call will dispose all items in 
/// the collection inside the same call stack. This avoids the .NET GC Finalizer thread from disposing the items
/// that were created and creating a GIL contention issue when other code is running.
/// </summary>
internal static class Pack
{
    internal static PyObject CreateTuple(Span<PyObject> items)
    {
        List<SafeHandleMarshaller<PyObject>.ManagedToUnmanagedIn> marshallers = new(items.Length);
        try
        {
            var handles = items.Length < 18 // .NET tuples are max 17 items. This is a performance optimization.
                ? stackalloc IntPtr[items.Length]
                : new IntPtr[items.Length];

            for (int i = 0; i < items.Length; i++)
            {
                SafeHandleMarshaller<PyObject>.ManagedToUnmanagedIn m = default;
                m.FromManaged(items[i]);
                marshallers.Add(m);
                handles[i] = m.ToUnmanaged();
            }
            return PyObject.Create(CPythonAPI.PackTuple(handles));
        }
        finally
        {
            foreach (var m in marshallers)
            {
                m.Free();
            }
        }
    }

    internal static PyObject CreateList(Span<PyObject> items)
    {
        PyObject pyList = PyObject.Create(CPythonAPI.PyList_New(0)); // TODO: preallocate based on items.Length and use PyList_SetItem

        foreach (var item in items)
        {
            int result = CPythonAPI.PyList_Append(pyList, item);
            if (result == -1)
            {
                throw PyObject.ThrowPythonExceptionAsClrException();
            }
        }

        return pyList;
    }

    internal static PyObject CreateDictionary(IEnumerable<PyObject> keys,  IEnumerable<PyObject> values) {
        PyObject pyDict = PyObject.Create(CPythonAPI.PyDict_New());

        IEnumerator<PyObject> keyEnumerator = keys.GetEnumerator();
        IEnumerator<PyObject> valueEnumerator = values.GetEnumerator();

        while (keyEnumerator.MoveNext() && valueEnumerator.MoveNext())
        {
            int result = CPythonAPI.PyDict_SetItem(pyDict, keyEnumerator.Current, valueEnumerator.Current);
            if (result == -1)
            {
                throw PyObject.ThrowPythonExceptionAsClrException();
            }
        }

        return pyDict;
    }
}
github-actions[bot] commented 1 month ago

Closed in 5423d6ba7cfc773a4b8a16d271f7cfa7592b006d