<p>This is pretty straightforward constant propagation, which enables the compiler to use more efficient instructions.</p>
<p>Since the value of <code>sizeof</code> is a constant known at compile time, in the version with the comparison the compiler knows the value of <code>typeSize</code> and therefore can use more efficient instructions.  Here’s for example the computation of <code>dynamicArr_Size(header)</code>, which is to say, <code>(header-&gt;end - header-&gt;begin) / header-&gt;typeSize</code>:</p>
<pre class="lang-diff prettyprint-override"><code>--- without sizeof
+++ with sizeof
-        mov     rax, QWORD PTR [rbx+8]
-        xor     edx, edx
-        sub     rax, QWORD PTR [rbx]
-        div     r8
+        mov     rdi, QWORD PTR [rbx+8]
+        sub     rdi, QWORD PTR [rbx]
+        shr     rdi, 2
</code></pre>
<p>In the version without <code>sizeof</code>, the compiler has to treat the element size as an unknown and use the actual divide instruction (which also requires zeroing the <code>rdx</code> register, as the instruction takes a 128-bit dividend in the <code>rdx:rax</code> register pair).  When the size is known, the compiler can substitute a much faster bit shift instead and avoid touching <code>rdx</code>.  This is probably the most impactful difference, as the divide instruction tends to be incredibly slow relative to other arithmetic; most compilers, whenever they have a chance (when the divisor is constant), will instead use bit shifts or <a href="https://arxiv.org/abs/2012.12369" rel="nofollow noreferrer">wrap-around multiplication tricks</a> just to avoid using the division instruction.  (Matt Godbolt has <a href="https://www.youtube.com/watch?v=bSkpMdDe4g4&amp;t=32m54s" rel="nofollow noreferrer">a whole section about division in his talk about compiler optimisations</a>.)  More efficient use of registers also frees up registers to be used in other places and may prevent spilling values into memory (though in this case it doesn’t seem to make much of a difference).</p>
<p>Another example, here’s how <code>sizeof(dynamicArr) + header-&gt;typeSize * newCapacity</code> is computed:</p>
<pre class="lang-diff prettyprint-override"><code>--- without sizeof
+++ with sizeof
-        mov     rdx, rsi
-        imul    rdx, r8
-        add     rdx, 32
+        lea     rdx, QWORD PTR [rsi*4+32]
</code></pre>
<p>In the version without comparison against <code>sizeof</code>, the compiler has to treat <code>header-&gt;typeSize</code> as an unknown and use a generic multiplication instruction.  When the size is known, it can instead use a <code>lea</code> instruction with a special addressing mode that allows to compute value in a single instruction.  Though generic multiplication is not as slow as division, a bit shift will still beat it.  But even if <code>lea</code> were not necessarily faster in itself, the higher code density allows more code to fit into the instruction cache and avoid slowdowns due to cache misses.</p>
<p>Finally, you claimed that</p>
<blockquote>
<p>A lot of people seem to think that the reason is because <code>sizeof item</code> is simply evaluated at compile time. I don't think that this is the case because if I remove the condition and replace all instances of <code>header-&gt;typeSize</code> with <code>sizeof item</code> the performance is still worse than if the if condition was there.</p>
</blockquote>
<p>I assume you have only replaced direct uses of the field inside the macro and not the indirect use inside the <code>dynamicArr_Size</code> utility function.  When you replace that use as well, <a href="https://godbolt.org/z/TT6endePG" rel="nofollow noreferrer">the generated code is nearly identical, modulo label names and the immediate value in the <code>if</code> condition check</a>.  With comparison against <code>sizeof</code>, the compiler simply does that automatically while inlining that function.</p>
Evaluating sizeof causes improvement in performance C
<p>I have a dynamic array and there is a hot loop that spends a lot of time adding a lot of elements to the dynamic array I have implemented.</p>
<p>The dynamic array works by storing a header at the start of the array of values:</p>
<pre class="lang-c prettyprint-override"><code>typedef struct
{
    unsigned char* begin; // Pointer to first element (should just point to end of struct, the header is stored prior data)
    unsigned char* end; // Pointer to last element
    size_t typeSize; // Size of type that the array stores
    size_t capacity; // capacity of array (when size ((end - begin) / typeSize) exceeds capacity, realloc)
} dynamicArr;
</code></pre>
<p>For some reason the loop runs faster when I compare the <code>sizeof item</code> to the <code>typeSize</code>, and its by a large margin (30% increase) as to when I don't make the comparison.</p>
<p>Note, the comparison is only there to safe keep against adding an item of different type and causing misalignment in the array. This should not happen anyway if the dynamic array is used properly to store 1 type and thus in practice should always evaluate to true.</p>
<p>Here's the code for adding an element to the list:</p>
<pre class="lang-c prettyprint-override"><code>if (arr)
{
    dynamicArr* header = dynamicArr_Header(arr);
    if (header-&gt;typeSize &amp;&amp; header-&gt;typeSize == sizeof item) //If I remove &quot;header-&gt;typeSize == sizeof item&quot; performance decreases.
    {
        size_t arrSize = dynamicArr_Size(header);
        if (arrSize == header-&gt;capacity)
        {
            size_t newCapacity = (size_t)(header-&gt;capacity * 1.5f);
            if (newCapacity == header-&gt;capacity) ++newCapacity;
            void* tmp = realloc(header, sizeof(dynamicArr) + header-&gt;typeSize * newCapacity);
            if (tmp)
            {
                dynamicArr_Construct(header, tmp, newCapacity, arrSize, header-&gt;typeSize);
                *((void**)&amp;(arr)) = header-&gt;begin;
                arr[arrSize] = item;
                header-&gt;end += header-&gt;typeSize;
            }
            else 
            { 
                free(header); 
                arr = NULL; 
            }
        }
        else
        {
            arr[arrSize] = item;
            header-&gt;end += header-&gt;typeSize;
        }
    }
}
</code></pre>
<p>I don't understand why this was the case. I'm not very good at reading assembly either, from what I can see though they are very different, so if someone could help me out here that would be much appreciated!</p>
<p>(Compiled in MSVC with /O2 and /Tc)</p>
<p><a href="https://godbolt.org/#z:OYLghAFBqd5TKALEBjA9gEwKYFFMCWALugE4A0BIEAZugHZEDKqAhgDbYgCMALOXUYBVAM7YACgA8QAcgAMM8gCse5dq3qgA%2BjtTkxnVEQINq2epgDC6dgFcAtvRAAmcucwAZAvWwA5BwBG2KQuAMzkAA7oIsQm9NZ2ji6R0bEMXj7%2B9kEhzuEG2EZxTESspEQJDk6uBUUMJWVEGX6BwWH6peWVSTWdTd4t2W15AJT66LakqFwyAKR5EaSswPasANQM07NyAILbe3neqHY4a7OhliJEhOgAdEjnuPvzoUcn2GcXV4SM94/Ph3ox1sp3Ol2u7AIAT%2BoSeuxebxBHzBV1I3mAMLhe12RAAnhFsDgaGtUbYjM8AOwAIWecgAnLZ6DFgD5MGtUEgygAqNZBYDec5UtYAemFa3E6G8RGCaxIa0wrFKtIZTIILMJ7M5pB57kFIrFEqlMrl6iua2wnHs5iIypiAC9sFoiLL8dgmAQHXrRWt3Q6NsS8QTbR7Hc62BFWKhiLivWLLKwI1G8f61mUlri1hB6OhnfRWqQUxbsFbGCI1t5U6R0yNKQAReW4%2BisewEVA7KuC2kHUJEgYNpstttVrTWJlEUhkohU1hiCA4K7kElTRc%2BADu8cT0ZG8uwZvO9ZEUz186IAFpHuHI9HPvW1xur3jj7uz48%2BRX95nGczWZruSMT2czhCva2DoMSXIngCPbYDQfaYI2zatu2pAjgwpJGHOz6LoeehrHeCYPrii6oJMpC%2Btgi6Bm6IbbvBA5IcOo7oVOM7YJhC5Lrh%2BGbniNahEKJ7nrC7g3juVxCbgb70IBQpUeRaw8hAJFVuRNbwvCeS9j4/aIUOKEABLYKwOCkBAabblAdG6chXIjFAX5qj%2BHJ/mZVbbqeJIhmBc4IYOyEjAF%2BxXIqrblvQkLaSBTo6X5w7kT59F6Ty5mUjS6l0qQ2BEJM0muaQEkiR5aYSVJ25isVjxySGnbwhStZdi8WkfFZsUoeItgiEgeWLsQxbbrMACslj7Jg6BnNSg3DelBDEnl/VDcqszUmcC3pdsdItQx2prEgRkmaJm16VohnGcEc16pNi30jNma7ad%2BWVa68nzAAbK9O17cEElVX6%2B4fiBYHltK9jzVN2J0utS1Cpda30utUXOmmz2hPWh3IVo8V3SZfHQ6t4OQ9ds1IyGN4fljX0XgRSa4qDV0Q3Dy0w/jcMs3SCN4dg65U9eH4QAjdnkw9sKXtTClrNwtwDTQOMrWDBP0wrN1Zpz96i39KMffdEki1uMnzFS3GERdePy/LABukqYDyRD2BEomZRw7DoKgECC9hXk0Al1luTJmsmd9T0kzyhvUzLTOm6zSs2xEtOwwrkOMybrMRxtvlbahY4ThhbuyrbK4q9zeKLsTDqLoLAcEqpxty8ntd0lyUAWwQVu2a9c39Rr5evtg/L0NX%2BxrIPQ/DyPI9poN04qdVA31h%2BvX2P3ccp13wkWHrnefULuA/dgi/M/HDOz0nB8KxaYjjUKNCZWxgsy2mom%2BEIHgeBddWy3TCdHzXJ9n8ix%2Bm4nMGo9gFD3HgNSeZFp6zw1vPPeKd1or1wCJfWZNN4V2op6fi78l6H2wfvJaX9Fpv3Dm/VcSACCcAgHINS2IdiigBjQfYUo1gADFGQuylDyKU/lUoDyHmjYc7VOoQG4VWRc3BqE1T2HVGQYx2CyAGooJw8hFDoFkJYEkEwpjIjyNwRQRBZAKACuQQW1AxgAGsQADTkGoWQvBFD2BALwOktwAAcoQ5DcGcLwAaFIBqhG4HwXg/BlEKHIGomQigRAgBsQYlRYw4CwBQBgHA%2BBiBkEoNQQQzA2CcB4PwbJogJDSBUcoZI6hNAgB0FoPQtRjCmAgO4boTgvGuLcBYZoWQcg8BeikGI9T4g2CqDwbgA0%2BlpHoJ0/MPT9BFjqPQBoXQhlJFabMwwAzFn9EyNM7gvTgpLMSC00ZHRGhTKGCEXZYwRBaOmLIF4ixlirA2ECXe6luyIlBF8a4JhMRQQ%2BciL5PwiC/I0q8IE7xPjgkwJCaEDxYR/PBUiSFqJ0QgtoVRIkJJxyTl4elBy6o2TOW2lJWM4pJSMGNGNBUSo8WqgJb%2BbauosHekNBSgsJoZzOiLCWG06V2Y71JfJQGVFgwOmijrR8zK4yFwzIDNMrAMxZhzHhfMhZLTWjLBWeVNM6wxS2lIqCTU9VHSYtiow05ZwnmwsuDmXMeI0zEs6f6R4sGCUpva0SIdoxPnEt3XuokID4qclqWyAF9aeQdIDCCz5DUwTgmnE1aEzVEHYkQa1XEC72uIqRcilFA4Ologm9Gpqs4sUtVhTi%2Bc7WERlm61ebIPx1skj3d8QEXSVyDpmZSkCC0NU0nG7SAiDKbzmpmL2rVbL2TpcGly5k1geQYeOraAUaHBWMKgMKEUPjsyHRjEMS6kqVlIDQqGypMrZVILlCq9b51HpKi2%2BgZU72PQ7ZgtK0j6pvMagO5qRbBEdS6mmHqwNY47FGhfcOhNMxzsgxDQBdMh08kFgdP9w77rnSwbB9aStEE71AwA3G395bsxLgC1GqG90OldpvMO/9I5Eynr9FGqCtbuprXg%2BB8GcEK3Zl65MvN%2BbUdY8LGVYsJZS1o0RuuSs%2BMZnVvWRBEqHUoKArJuBdd1pNytrnO2H4HbsCdi7HOi6h39Tbbh/NHxg6Zprepk%2B2HZrR3wxphmhGP5113SWycQmTKUTzra1W0Zi6MYon7CmsI8N2fgfSBuEAtNclbs4F67dRKIJJZhvGICsugI7OA0jg1oH1lgRlqT9n6SIOQUBFj/sX0YN3iV9z8diF0Z/uwc%2BUM1hX2wDfGjep74fkfs/V%2BhDuMEI43XX%2B437NcZ2Nl0eYCIHkQK6JYrbnRvlbQY8SrNIN7Ce3pZqLydmulbG1h47uqyEULYpIt5opswRtAow3YzC2FAhEYwLhjAeG1XfcPXdQiuqiIoOLSR/E6yyNsTIRR5BQmqPUZoyY0xAKhGcPowxsiximIgHIuxDirFyFuLsukri/G8G8S9VxriBoDV6bD8JsgokxPIHEoxFiQChF4G44nzhAk04pBSFHXjIehCUej%2BnkTmfo4SYgRJyA0DoFtldigVAlIK4iErtAxxuB0i0C9ApFDpSkGiRAAIYuAjeDKLiWQejyAYHsDygA8uFK3pScCrE0Hk13BBMpFDNruMX2BJCFFsNKa3igjTyNKTCpYpBcTWBwGL8cBBHHxIEAwHJHAuB8DT8IMQUgxcqG4GoDQ2hdBqChNEyAYx0ARAGdE4UDuahzIGWYCwzTVDuDOd0ovUR%2BlxHbz31IAyu9tCL3U4ofQB9rMKBsvoI%2BLknIOcMsfc%2BBhdNH1cm5WecdQ9F6UiJkg9drHsCIM2G6zba8J3ITMAB1AAkr4XAFk0kkALC8IvaxrCK84G/3R25LBo7xJs7OBjKR72LkCOLWIw5i4RKM6xJS7kCy6uzy7f7BCZKq6oEhCoBa46564CAG7BDG6m6lLm5Nix5h624K6O7O5i5u4l6e5hKEA%2B7GB%2B7RKlKB7B6h4yA24R5i7R6W7x4zBhJJ4p56JjDZIsCZ75I55EBFL56lIqCuAVKl41Ll4BCV7Y7hK15xD16N7T7zKt5WDLItLtKeBr47LjIDJT694TLz6qDj71CT7GH2HN4T6nLmHnL2FOGHJeHuHbKeHcCb6I5cDOA77Q504H5H4n5n5rAX4uISK34P5P6Zgv5kDI6uCf5q5K7pH/6AGs4mKbxmKQ7gGQE2IREM76BM4s4Y7kCWIgG3Ac50ihAU50i8AUhyD84Ui9KR4i7QH74VHVHS7wDIGEA0A0DoESG5JZ4FLp5yElJhLsBIDRJF6LEiCjE0BURM6kBLHJDbFrEzQbGugxJhF75hIRK1gHFrCH68DH6n7n6X4JEQD36P7bhmxljXG3ExFxFX6JEvF5E1GWKhAuJ%2BLeLa68BU7a6eLeLFGnFw4S7RLwFAGQ6o59FnEDFS5jB%2B5G5xBOJAA" rel="nofollow noreferrer">Link</a> to the assembly and the rest of the relevant code.</p>
<p><strong>Edit 1:</strong>
<s>A lot of people seem to think that the reason is because <code>sizeof item</code> is simply evaluated at compile time. I don't think that this is the case because if I remove the condition and replace all instances of <code>header-&gt;typeSize</code> with <code>sizeof item</code> the performance is still worse than if the <code>if</code> condition was there.</s> =&gt; I seem to have missed changing the use of <code>header-&gt;typeSize</code> in the macro <code>dynamicArr_Size</code> which caused this confusion, refer to the marked answer.</p>
<p>Here is the full code:</p>
<pre class="lang-c prettyprint-override"><code>typedef struct
{
    unsigned char* begin; // Pointer to data
    unsigned char* end; // Pointer to last element
    size_t typeSize; // Size of type
    size_t capacity; // Capacity of array (not number of elements in array)
} dynamicArr;

#define dynamicArr_ConstructBase(dest, src, newCapacity) dest = src; dest-&gt;capacity = newCapacity; dest-&gt;begin = (unsigned char*)dest + sizeof *dest
#define dynamicArr_Construct(dest, src, newCapacity, currSize, typeSize) dynamicArr_ConstructBase(dest, src, newCapacity); dest-&gt;end = dest-&gt;begin + typeSize * (currSize)

#define dynamicArr_Header(arr) ((dynamicArr*)((unsigned char*)(arr) - sizeof(dynamicArr)))
static inline size_t dynamicArr_Size(dynamicArr* arr)
{
    return (arr-&gt;end - arr-&gt;begin) / arr-&gt;typeSize;
}

#define dynamicArr_Create(typename, arr) typename* arr = (typename*)dynamicArr_Create_(sizeof(typename))
static inline unsigned char* dynamicArr_Create_(size_t typeSize)
{
    dynamicArr* dynArr;
    void* tmp = malloc(sizeof * dynArr + typeSize * 10);
    if (!tmp) return NULL;

    dynArr = tmp;
    dynArr-&gt;begin = (unsigned char*)dynArr + sizeof * dynArr;
    dynArr-&gt;end = dynArr-&gt;begin;
    dynArr-&gt;capacity = 10;
    dynArr-&gt;typeSize = typeSize;

    return dynArr-&gt;begin;
}

#define dynamicArr_Free(arr) free(dynamicArr_Header(arr))

#define dynamicArr_Push(arr, item) \
do {\
if (arr) \
{ \
    dynamicArr* header = dynamicArr_Header(arr); \
    if (header-&gt;typeSize &amp;&amp; header-&gt;typeSize == sizeof item) \
    { \
        size_t arrSize = dynamicArr_Size(header); \
        if (arrSize == header-&gt;capacity) \
        { \
            size_t newCapacity = (size_t)(header-&gt;capacity * 1.5f); \
            if (newCapacity == header-&gt;capacity) ++newCapacity; \
            void* tmp = realloc(header, sizeof(dynamicArr) + header-&gt;typeSize * newCapacity); \
            if (tmp) \
            { \
                dynamicArr_Construct(header, tmp, newCapacity, arrSize, header-&gt;typeSize); \
                *((void**)&amp;(arr)) = header-&gt;begin; \
                arr[arrSize] = item; \
                header-&gt;end += header-&gt;typeSize; \
            } \
            else  \
            {  \
                free(header);  \
                arr = NULL;  \
            } \
        } \
        else \
        { \
            arr[arrSize] = item; \
            header-&gt;end += header-&gt;typeSize; \
        } \
    } \
} \
} while(0)
</code></pre>
<p>And example use:</p>
<pre class="lang-c prettyprint-override"><code>void Func()
{
    dynamicArr_Create(int, intArr);
    dynamicArr_Push(intArr, 10);
    printf(&quot;%i\n&quot;, intArr[0]);
    dynamicArr_Free(intArr);
}
</code></pre>
<p>As for a simple test for profiling:</p>
<pre class="lang-c prettyprint-override"><code>int main()
{
    dynamicArr_Create(int, intArr);

    clock_t begin = clock();

    for (int i = 0; i &lt; 1000000000; ++i)
    {
        dynamicArr_Push(intArr, 10);
    }

    clock_t end = clock();
    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf(&quot;%f\n&quot;, time_spent);

    dynamicArr_Free(intArr);
}
</code></pre>
<p>Compiling in release mode in Visual Studio 2019 on windows using /Tc I get the results:</p>
<ul>
<li>With <code>header-&gt;typeSize == sizeof item</code> =&gt; 3.65 seconds</li>
<li>Without <code>header-&gt;typeSize == sizeof item</code> =&gt; 9.374 seconds</li>
<li>Replacing <code>header-&gt;typeSize</code> with <code>sizeof item</code> and removing <code>header-&gt;typeSize == sizeof item</code> =&gt; 9.302 seconds</li>
</ul>
<p>I repeated the test 10 times and it was consistent to the results above.</p>