hpyproject / hpy

HPy: a better API for Python
https://hpyproject.org
MIT License
1.02k stars 52 forks source link

Protocols to remove abstraction overhead #400

Open steve-s opened 1 year ago

steve-s commented 1 year ago

Protocols can help remove abstraction overhead when possible. For example, consider the case of iterating over a sequence (list, tuple, array.array, etc.) that happens to contain integers.

Design goals

Considering the iteration protocol, which would look as follows:

void iterate_objects()
{
    /* If the object is not a sequence, we might want to fall back to generic iteration. */
    HPySequence seq = HPy_AsSequence(ctx, obj);
    if (HPy_Sequence_IsError(seq))
        goto not_a_sequence;
    HPy_Close(ctx, obj);   /* we'll be using only 'seq' in the sequel */
    HPy_ssize_t len = HPy_Sequence_Len(ctx, seq);
    for (int i=0; i<len; i++)
    {
        /* HPy_Sequence_GetItem will check a flag on seq to see if it can use a
           fast-path of direct indexing or it needs to go through a generic
           fallback. And the C compiler will hoist the check out of the loop,
           hopefully */
        HPy item = HPy_Sequence_GetItem(ctx, seq, i); /* like PyList_GET_ITEM */

        /* process 'item' */
        HPy_Close(ctx, item);
    }
    HPySequenceClose(ctx, seq);

not_a_sequence:
    HPy iterator = HPy_GetIter(ctx, obj);
    HPy_Close(ctx, obj); /* we have 'iterator' */
    while (true) {
        HPy item = HPy_IterNext(ctx, iterator);
        if (HPy_IsError(item)) goto oops;
        if (HPy_IsIterStop(item)) break;
        /* process 'item' */
        HPy_Close(ctx, item);
    }
    HPy_Close(ctx, iterator);
}

Optimised variant when a sequence of C long integers is expected:

void iterate_long()
{
    /* This is allowed to fail and you should be ready to handle the fallback. */
    HPySequence_long seq = HPy_AsSequence_long(ctx, obj);
    if (HPy_Sequence_IsError_long(seq))
        goto not_a_long_sequence;
    HPy_Close(ctx, obj);
    HPy_ssize_t len = HPy_Sequence_Len_long(ctx, seq);
    for (int i=0; i<len; i++) {
        long item = HPy_Sequence_GetItem_long(ctx, seq, i);
        /* process 'item' */
    }
    HPySequenceClose_long(ctx, seq);

not_a_long_sequence:
    /* ... */
}

(copied from docs/misc/protocols.rst)