Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions mypyc/lib-rt/CPy.h
Original file line number Diff line number Diff line change
Expand Up @@ -862,11 +862,44 @@ bool CPySet_Remove(PyObject *set, PyObject *key);

// Tuple operations


PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index);
PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end);
PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index);
void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, PyObject *value);

static inline bool CPySequenceTuple_IsValidIndex(Py_ssize_t n, Py_ssize_t size) {
return (0 <= n && n < size) || (-size <= n && n < 0);
}

static inline PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks kind of a large function to inline -- it can slow down compilation and/or increase size of generated code, which can cause L1 cache misses. Also I've seen that compilers sometimes don't want to inline a function if it's big enough, even if declared as inline.

We could instead only inline the fast path -- non-negative, short integer index, no error. The remaining cases would be handled by another, non-inlined function. You can find an example of this in CPyList_GetItem and CPyList_GetItem_.

{
if (likely(CPyTagged_CheckShort(index))) {
Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
Py_ssize_t size = PyTuple_GET_SIZE(tuple);
if (unlikely(!CPySequenceTuple_IsValidIndex(n, size)))
{
PyErr_SetString(PyExc_IndexError, "tuple index out of range");
return NULL;
}
if (n < 0)
n += size;
PyObject *result = PyTuple_GET_ITEM(tuple, n);
Py_INCREF(result);
return result;
} else {
PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
return NULL;
}
}

static inline PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index)
{
PyObject *result = PyTuple_GET_ITEM(tuple, index);
Py_INCREF(result);
return result;
}

static inline void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, PyObject *value)
{
PyTuple_SET_ITEM(tuple, index, value);
}


// Exception operations
Expand Down
40 changes: 0 additions & 40 deletions mypyc/lib-rt/tuple_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,6 @@
#include <Python.h>
#include "CPy.h"

PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index) {
if (CPyTagged_CheckShort(index)) {
Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
Py_ssize_t size = PyTuple_GET_SIZE(tuple);
if (n >= 0) {
if (n >= size) {
PyErr_SetString(PyExc_IndexError, "tuple index out of range");
return NULL;
}
} else {
n += size;
if (n < 0) {
PyErr_SetString(PyExc_IndexError, "tuple index out of range");
return NULL;
}
}
PyObject *result = PyTuple_GET_ITEM(tuple, n);
Py_INCREF(result);
return result;
} else {
PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
return NULL;
}
}

PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) {
if (likely(PyTuple_CheckExact(obj)
&& CPyTagged_CheckShort(start) && CPyTagged_CheckShort(end))) {
Expand All @@ -45,18 +20,3 @@ PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged en
}
return CPyObject_GetSlice(obj, start, end);
}

// No error checking
PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index)
{
PyObject *result = PyTuple_GET_ITEM(tuple, index);
Py_INCREF(result);
return result;
}

// PyTuple_SET_ITEM does no error checking,
// and should only be used to fill in brand new tuples.
void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, PyObject *value)
{
PyTuple_SET_ITEM(tuple, index, value);
}
Loading