Wo liegt der Fehler?



  • Hoi,
    ich wollte mal die Performance von einer trivialen Eigenimplementierung und std::memset testen.

    #include <cstring>
    #include <cstdint>
    #include <iostream>
    
    template<typename IntegerT>
    void memset_impl(IntegerT* ptr, IntegerT value, std::size_t num)
    {
        for(IntegerT* end = ptr + num; ptr != end; ++ptr)
            *ptr = value;
    }
    
    void my_memset(void* ptr, std::uint8_t value, std::size_t num)
    {
        std::uint8_t* data = static_cast<std::uint8_t*>(ptr);
    
        if(num <= sizeof(std::uint64_t) * 2)
        {
            memset_impl<std::uint8_t>(data, value, num);
        }
        else
        {
            std::size_t rest = num % sizeof(std::uint64_t);
            if(rest)
            {
                memset_impl<std::uint8_t>(data, value, rest);
                num -= rest;
                data += rest;
            }
    
            std::uint64_t pattern;
            auto pattern_ptr = reinterpret_cast<std::uint8_t*>(&pattern);
            memset_impl<std::uint8_t>(pattern_ptr, value, sizeof(pattern));
    
            auto new_ptr = reinterpret_cast<std::uint64_t*>(data);
            memset_impl<std::uint64_t>(new_ptr, pattern,
                num / sizeof(std::uint64_t));
        }
    }
    
    int main()
    {
        char* huge = new char[50];
        my_memset(huge, '*', 49);
        huge[49] = 0;
        std::cout << huge;
    }
    

    Der GCC macht mit O0, O1, O2 keine Probleme, bei O3 kracht es aber. Mit Debug-Ausgaben in memset_impl nichtmal dann.
    Bin gerade hundemüde, warscheinlich ist es etwas Triviales, aber wo erzeuge ich UB?

    Debuggen hat mir nicht wirklich etwas gebracht. Fliegt bei einem

    movdqa %xmm0,(%rcx)
    

    in die Luf, dh. wenn der Compiler eigenständig XMM verwendet.

    Danke & Grüße,
    Ethon


  • Mod

    Ethon schrieb:

    wo erzeuge ich UB?

    Hier:

    auto new_ptr = reinterpret_cast<std::uint64_t*>(data);
    

    Durch das data += rest; ist praktisch garantiert, dass data nicht richtig ausgerichtet ist. (Auch ohne das data+=rest müsste die Funktion selbst für die korrekte Ausrichtung sorgen, um allgemein einsetzbar zu sein).



  • Eigentlich ist es drei Zeilen weiter unten:

    auto new_ptr = reinterpret_cast<std::uint64_t*>(data);
    

    Aber ansonsten so, wie camper es beschreibt.


  • Mod

    err, ja, habe versehentlich die falsche Zeile kopiert.



  • Danke für die Antworten. 😉

    (Auch ohne das data+=rest müsste die Funktion selbst für die korrekte Ausrichtung sorgen, um allgemein einsetzbar zu sein).

    Das heißt wohl dass es nicht genügt rest einfach danach zu schreiben?
    Was wäre denn korrekt? Mit alignof eine "Basis" für uint64 finden, Bytes bis zu dieser Basis schreiben, möglichst viele uint64s schreiben und dann den Rest wieder byteweise?



  • Genau das.


Anmelden zum Antworten