Search code examples
cinline

Does inline create a COPY of the parameters as variables or will it reuse the same from the input?


Here what this problem looks like:

typedef uint32_t StrTableKey_t;

typedef struct StrTableEntry_s
{
    char *string;
    StrTableKey_t keyhash, keyid;
    size_t usages;
} StrTableEntry_t;

typedef struct StrTable_s
{
    StrTableEntry_t *entries;
    StrTableKey_t count;
    StrTableKey_t capacity;
} StrTable_t;

inline bool StrTableBinarySearchUnsafe (const StrTable_t *table, StrTableKey_t keyid, size_t *output)
{
    size_t idxlow = 0, idxhigh = table->count - 1;

    while (idxlow <= idxhigh)
    {
        size_t idxmid = (idxlow + idxhigh) >> 1;

        if (table->entries[idxmid].keyid == keyid)
        {
            (*output) = idxmid;

            return true;
        }
        else if (table->entries[idxmid].keyid > keyid)
        {
            // prevents overflow bug.
            if (idxmid == 0)
            {
                break;
            }

            idxhigh = idxmid - 1;
        }
        else
        {
            idxlow = idxmid + 1;
        }
    }

    return false;
}

char* StrTableGetValueByKey (const StrTable_t *table, StrTableKey_t keyid)
{
    size_t idx;

    if (table->count != 0 && StrTableBinarySearchUnsafe(table, keyid, &idx))
    {
        return table->entries[idx].string;
    }

    return NULL;
}
  • The StrTableBinarySearchUnsafe is a common iteration to find an item inside the table and is used multiple times in the code.

  • From what I understand about inline, the compiler optimization will put all the instructions from StrTableBinarySearchUnsafe into StrTableGetValueByKey but those argouments that are passed (table, keyid, &idx) will they be copied and defined twince as new variables or will the compiler use the same variables?

  • If the argouments are copied the stack call of StrTableGetValueByKey should take almost twince his argouments, if they are not copied then if _StrTableBinarySearchUnsafe changes them it will take effect into StrTableGetValueByKey.

  • I have checked both with and whitout the compiler flag -O3 and it looks like they are copied or the compiler is smart enough to know what value should be copied, could I get confirmation about it?

EDIT 1

Thanks to @UlrichEckhardt to point out I was breaking the private indentifier rule documented here: https://en.cppreference.com/w/c/language/identifier

  • Underscore with uppercase name defined for _StrTableBinarySearchUnsafe shouldn't be used, it has been renamed to StrTableBinarySearchUnsafe.
  • IIRC undescore whitout the uppercase are commonly used to mark something to be used privately inside a translation unit, I will keep the suffix Unsafe to do the same instead.

Solution

    • From what I understand about inline, the compiler optimization will put all the instructions from StrTableBinarySearchUnsafe into StrTableGetValueByKey

    The compiler might do so, or it might not. The inline does not require it to do. And the compiler might or might not do the same even for functions declared without inline. Do not be confused by the fact that the optimization you describe is called "inlining" -- the inline keyword was inspired by inlining, but it does not require the compiler to perform inlining.

    but those argouments that are passed (table, keyid, &idx) will they be copied and defined twince as new variables or will the compiler use the same variables?

    If the compiler chooses to inline a function call, it is responsible for ensuring that the observable behavior is identical to what would be observed if it did not do so. Execution time may differ, however, and all manner of details that are not classified as "observable behavior" may do as well. C semantics are defined in terms of an abstract machine, and in the abstract machine, function arguments are copied in (shallowly). But although that often influences observable behavior, it is not itself classified as observable behavior.

    The bottom line, then, is that C does not answer the specific question posed, and implementations might not even be consistent about it, but, generally speaking, you should not care.

    • If the argouments are copied the stack call of StrTableGetValueByKey should take almost twince his argouments, if they are not copied then if _StrTableBinarySearchUnsafe changes them it will take effect into StrTableGetValueByKey.

    Those are among the considerations that an optimizing compiler may use in choosing how to optimize a function call or function implementation. There are many more. If you actually care about the details in specific cases then C probably is not the language for you. One of the main points of using a high-level language is that you don't have to sweat those details.

    With that said, optimizing compilers typically provide a variety of options for influencing optimization strategy, perhaps even locally. This usually goes well beyond mere optimization level (-O1, -O2, etc). Consult your compiler's documentation if you really want to go down that rabbit hole.

    • I have checked both with and whitout the compiler flag -O3 and it looks like they are copied or the compiler is smart enough to know what value should be copied, could I get confirmation about it?

    Confirmed. See above.

    • IIRC undescore whitout the uppercase are commonly used to mark something to be used privately inside a translation unit, I will keep the suffix Unsafe to do the same instead.

    Identifiers that are to be used only inside one translation unit should be declared static, which has exactly the effect of providing such isolation. Some projects do apply one or another Hungarian notation, which might include adorning static identifiers in some distinguishing way, but from where I sit, that does not appear to be particularly common in C. There are such conventions among practitioners of some other languages (Python comes to mind), but it is not reasonable to project that onto C.