C++Guns – RoboBlog blogging the bot

02.03.2019

C++ Guns: A Sorted Data Type

Filed under: Allgemein — Tags: — Thomas @ 18:03

Mit dem C++ Typsystem ist es möglich Informationen und Algorithmen in das Programm zu codieren, die schon vor der Compilezeit fest stehen.

Wie würdet ihr folgendes Problem lösen:
Ein Algorithmus, zum Beispiel die binäre Suche, benötigt einen bereits sortieren Datensatz. Den Datensatz zu sortieren und danach die Suche anzuwenden ist trivial. Aber in einem anderen Teil des Programms muss danach noch einmal die binäre Suche angewendet werden. Nur kann dieser andere Programm Teil nicht annehmen, dass der übergebene Datensatz sortiert ist. Noch einmal die Sortierung angewandt kostet viel Laufzeit. Zu testen ob die Daten bereit sortiert sind, ist immer noch ein linearer Algorithmus.
Der Programmierer könnte auch eine bool Variable mit geben, die aussagt ob die Daten bereits sortiert sind. Das ist immerhin nur ein konstanter Mehraufwand. Der Datensatz muss stark mit dieser Statusvariable gekoppelt sein, damit keine Inkonsistenz entsteht. Also kommen beide zusammen in einem neuen Datentyp. Wenn man den sortierten Datensatz noch read-only macht, muss man kein Aufwand treiben festzustellen, ob der Datensatz durch ein Schreibzugriff nun nicht mehr sortiert ist. Damit ist auch die Statusvariable konstant.

Jetzt passiert etwas besonderes. Die Statusvariable wird von einer Funktion erzeugt und hat immer den selben Wert. Nämlich true, für "sortiert". Damit ist es möglich, diese Idee auch zur Compilezeit auszuführen und einen neuen Datentype Sorted einzuführen, der nur von einer sort() Funktion erzeugt werden kann und IMMER sortierte Daten enthält. Die bool Variable fällt komplett weg. Ihre Semantik ist im neuen Datentyp kodiert.

Wie sieht diese Idee in der Praxis aus? Es braucht eine sorted() Funktion welche die Daten sortiert und einen Sorted Typ zurück gibt. Damit, bei Bedarf, keine unnötigen Kopien erstellt werden, ist es auch möglich mit moveOwnership() die Daten in den Sorted Typ zu verschieben.

Jeder Algorithmus welcher sortiere Daten benötigt, soll nur Datensätze von Typ Sorted akzeptieren. Damit ist zur Compilezeit sichergestellt, dass nie aus Versehen unsortierte Daten übergeben werden und es wird nur einmal der Datensatz sortiert. Und alles kommt dank statischer Typisierung mit zero-overhead.

std::vector<int> vec {3,2,1};
// sort and move ownership to Sorted
Sorted sortedVec = sorted(acpl::moveOwnership(vec));

// can be used with the strong typed binary_search function
binary_search(sortedVec, valueToSearch);

Der Type Sorted implementiert nur ein read-only Interface und ist ein Wrapper um einen beliebigen Container. Der Konstruktor ist privat, damit nur die spezielle sorted Funktion diesen Typen erstellen kann.

auto nope = Sorted<std::vector<int>>{vec}; // error: ‘Sorted<Container>::Sorted(Container&&) is private within this context

Für bestehende Algorithmen können leicht Wrapper geschrieben werden. Wie beispielsweise für die binäre Suche:

template<typename Container, typename T>
bool binary_search(const Sorted<Container>& data, const T& value) {
    return std::binary_search(data.begin(), data.end(), value);
}

Die Implementierung der sorted() Funktion ist relativ einfach, wenn die Sache mit Universal Referenz geklärt ist

template<typename Container>
Sorted<std::remove_reference_t<Container>> sorted(Container&& data) {
    // variable data is a univeral reference. pattern: "deducted type&&" so we use forward
    std::remove_reference_t<Container> sorted(std::forward<Container>(data));
    std::sort(sorted.begin(), sorted.end());
    // variable sorted is not a universal reference so we use move
    return {std::move(sorted)};
}

Natürlich gibt es auch einen overload für die Funktion isSorted() diese gibt trivialierweise immer true zurück.

template<typename Container>
bool isSorted(const Sorted<Container>&) {
    return true;
}

Und die Implementierung des Typ Sorted selbst

template<typename Container>
// requires Sequence container
class Sorted {


    Sorted(Container&& data)
        : c(std::forward<Container>(data))
    {  }

public:
    using value_type = typename Container::value_type;
    using const_iterator = typename Container::const_iterator;
    using const_reference = typename Container::const_reference;

    auto size() const {
        return std::size(c);
    }

    const_iterator begin() const {
        return c.begin();
    }

    const_iterator end() const {
        return c.end();
    }

    const_reference at(const auto& i) const {
        return c.at(i);
    }

    const_reference operator[](const auto& i) const {
        return c[i];
    }

    const_reference front() const {
        return c.front();
    }

    template<typename C>
    friend Sorted<std::remove_reference_t<C>> sorted(C&& data);

    const Container c;
};

22.02.2019

C++ Guns: rvalue referenz, forwarding referenz, universal referenz WTF?

Filed under: Allgemein — Tags: — Thomas @ 18:02

Die gute Nachricht: Normale Menschen müssen das alles nicht verstehen. Auch Anwendungs- Programmierer und library designer brauchen die Details nicht zu wissen. Ihr könnt meine Regeln durchlesen, daran halten und dann STOP.
Die Details sind für Leute, die sich einem 1.5h Vortrag über && anhören können.

Streng genommen wird der Begriff "universal referenz" nicht im C++ std benutzt. Dies ist ein Begriff von Scott Meyers, der ihn benutzt um die Ding begreiflich zu machen. Ich stimme mit ihm überein und benutze in diesem Text nicht den Begriff "forwared referenz" sondern "universal referenz". Viele sagen C++ sei so schon kompliziert genug. Man muss es ja nicht noch schlimmer machen. Weiterhin werde ich Abkürzungen benutzen. rvalue referenz Rref. universal referenz Uref.

Nun, Rref und Uref tauchen (auch) bei ownership transfer auf. Das ist IMO die einzige Stelle bei der man das braucht. Beispiele hierzu in C+ Guns: ACPL ownership transfer.
Nun sehen sich Rref und Uref von der Syntax her ziemlich gleich. Beide haben einen Typ und dann das doppele Kaufmanns-Und, Et-Zeichen &&. Nicht zu verwechseln mit dem logischen AND Operator. Aber für diesen nutzt ihr selbstverständlich and und nicht &&.
Es kommt nun auf den Typ an. Wird er vom Compiler hergeleitet (deduction) und ist es GENAU (im Sinne von EXACT) die Form T&&, dann ist es Uref, sonst Rref. Der Typ T ist ein Template Parameter und sein Name natürlich frei wählbar. Aber sobald ein const hinzugefügt wird, verschwindet das Uref und das Rref taucht auf.

Im ersten Beispiel (in dem verlinkten Artikel) ist somit Rref am Werk. Im zweiten Beispiel Uref. Und im dritten garnichts, weil man von const Variablen nicht moven kann!

Funktionen überladen mit Rref und Uref ist so eine Sache. Uref ist eine _*UNIVERSAL*_!!! Referenz. Sie behandelt ALLES. Und damit ist exakt alles gemeint. Nun ist es technisch (Stand 02.2019 pre C++20) möglich, eine Funktion zu überladen, so dass die eine Rref und die andere Uref akzeptiert. Aber das macht keinen Sinn und ist meiner Meinung nach ein Compilezeit Fehler.

Funktionen mit Rref und "normalen" Referenzen zu überladen ist natürlich möglich, gewollt und hilfreich. Möchte man mit den einen doch etwas anderes machen als mit den anderen (move vs. read only). Und da sich Rref und Uref ähnlich sehen, viel Spass!

Jetzt wird aus jedem Rref und Uref wieder ein ganz normales lvalue. Weil die haben einen Variablennamen und eine Adresse. Zwangsläufig, sonst könnte man den Variablennamen beim programmieren ja nicht eintippen -_-
Also muss wann immer eine Rref oder Uref Variable weiter benutzt wird, ein std::move oder std::forward kommen. Für Rref, also move Operationen nutzen wir std::move. Für Uref, also move oder copy Operationen nutzen wird std::forward. Benutzen wir keines von beiden, ist das meiner Meinung nach ein Compilezeit Fehler. Jemand sollte ein GCC PLugin schreiben.

Regeln:

  • Uref und Rref braucht man (unter anderem) für ownership transfer. Ihr werde es kaum brauchen.
  • Uref genau wenn T ein template parameter und exact die Form T&& vorliegt, ohne const ö.ä, sonst Rref.
  • T&& verhält sich in der Praxis NICHT wie Rref. Es kommt auf T an.
  • Wenn immer T&& im Code auftaucht: Gelber ALARM! Braucht man das wirklich? Wird überall std::move() oder std::forward() angewandt?
  • Von const Variablen kann man nicht moven.
  • Funktionen überladen mit Uref -> Fehler
  • Rref und normale Referenzen überladen -> Gut
  • Rref -> std::move
  • Uref -> std::forward
  • Soll ownership tranfer UND eine Kopie möglich sein, macht die Kopie explizit im Code deutlich:
  •  // v ist KEINE uref, da kein template Parameter
    auto func(std::vector && v) 
    { }
    
    std::vector v;
    func(acpl::moveOwnership(v)); // ownership transfer
    func(std::vector(v)); // Kopie
    

    STOP

    Für weitere Informationen zu auto&&, decltype(x), decltype((x)) und sonstige Schmerzen schau euch den Vortrag von Scott Meyers an. Er ist wirklich gut!
    C++ and Beyond 2012: Scott Meyers - Universal References in C++11

    C+ Guns: ACPL ownership transfer

    Filed under: Allgemein — Tags: — Thomas @ 18:02

    acpl::moveOwnership bietet im Gegensatz zu std::move den Vorteil, dass ein move-from const Variablen einen Fehler zur Compilezeit erzeugt.

    Zwei Beispiele sollen ownership transfer verdeutlichen.
    Daten in ein Type moven und dort speichern.

    #include <core/util/functional.hpp>
    
    struct Node {};
    struct Edge {};
    struct Graph {
        std::vector<Node> nodes;
        std::vector<Edge> edges;
    
        Graph(std::vector<Node>&& nodes, std::vector<Edge>&& edges) 
        : nodes(acpl::moveOwnership(nodes)), edges(acpl::moveOwnership(edges))
        {
        }
    };
    
    void func() {
        std::vector<Node> nodes;
        std::vector<Edge> edges;
        // ... fill nodes and edges
        // graph is the new ownership of nodes and data
        Graph graph(acpl::moveOwnership(nodes), acpl::moveOwnership(edges));
        assert(nodes.empty());
        assert(edges.empty());
    }
    

    Daten entweder kopieren oder durch eine Funktion moven.

    #include <core/util/functional.hpp>
    
    template<typename Container>
    inline std::vector<int> copyOrMove(Container&& x) {
        Container data2(std::forward<Container>(x));
        return data2;
    }
    
    void func() {
        std::vector<int> x {1,2,3};
        std::vector<int> copy = copyOrMove(x);
        assert(not x.empty());    
        std::vector<int> moved = copyOrMove(acpl::moveOwnership(x));
        assert(x.empty());    
    }
    

    Move von const Variablen ist nicht möglich, da die Variablen geändert werden würden, was nicht möglich ist, weil sie const sind.

    void func() {
        const std::vector<int> x {1,2,3};
        std::vector<int> moved = acpl::moveOwnership(x); // error: static assertion failed: Move from const variables dosent make sense
    }
    

    15.02.2019

    C++ Guns: class template specialization with concepts aka C++20 std::ranges::value_type

    Filed under: Allgemein — Tags: — Thomas @ 20:02

    You can create a constrain for a class. And another constrain for the same class. Isn't this crazy stuff?

    With this we can build something like an identity function for types, like std::type_identity, for value_type. It's something like the C++17 non-member functions size() and empty(). Why must value_type be a member? It can be a non-member trait.

    It turns out, there is already this functionality in C++ 20 ranges TS in std::experimental::ranges::value_type. It's really hard to catch up... but implemented it for you. And users may specialize value_type I also put my part for arithmetic types here.

    This is part of ACPL functional.hpp file.

    /// Primary template is an empty struct.
    /// \note this is in C++20 ranges now except for the arithmetic types overload 
    /// https://en.cppreference.com/w/cpp/experimental/ranges/iterator/value_type
    template<class I>
    struct value_type { };
    
    /// Specialization for pointers.
    /// If T is an object type, provides a member type type equal to std::remove_cv_t<T>.
    ///  Otherwise, there is no member type.
    template<class T>
    struct value_type<T*> {
      using type = std::remove_cv_t<T>;
    };
    
    /// Specialization for array types.
    template<class I>
      requires(std::is_array<I>::value)
    struct value_type<I> : value_type<std::decay_t<I>> { };
    
    /// Specialization for const-qualified types.
    template<class T>
    struct value_type<const T> : value_type<std::decay_t<T>> { };
    
    /// Specialization for types that define a public and accessible member type value_type.
    /// If T::value_type is an object type, provides a member type type equal to T::value_type.
    /// Otherwise, there is no member type.
    /// \todo requires requires
    template<class T>
      requires requires{ typename T::value_type; }
    struct value_type<T> {
      using type = typename T::value_type;
    };
    
    /// Specialization for types that define a public and accessible member type element_type.
    /// (e.g., std::shared_ptr).
    /// If T::element_type is an object type, provides a member type type equal to std::remove_cv_t<typename T::element_type>.
    /// Otherwise, there is no member type.
    template<class T>
      requires requires{ typename T::element_type; }
    struct value_type<T> {
        using type = typename T::element_type;
    };
    
    /// Helper alias template
    template<class T>
    using value_type_t = typename value_type<T>::type;
    
    /// ACPL specialization for arithmetic types
    template<class T>
      requires(std::is_arithmetic_v<T>)
    struct value_type<T> {
        using type = T;
    };
    
        // Specialization for pointers.
        static_assert(std::is_same_v<int, acpl::value_type_t<int*>>);
        // Specialization for array types.
        static_assert(std::is_same_v<int, acpl::value_type_t<int[]>>);
        // Specialization for const-qualified types.
        static_assert(std::is_same_v<int, acpl::value_type_t<const int*>>);
        // Specialization for types that define a public and accessible member type value_type.
        static_assert(std::is_same_v<int, acpl::value_type_t<std::array<int,1>>>);
        // Specialization for types that define a public and accessible member type element_type.
        static_assert(std::is_same_v<int, acpl::value_type_t<std::unique_ptr<int>>>);
    
        // ACPL specialization for arithmetic types
        static_assert(std::is_same_v<int, acpl::value_type_t<int>>);
        static_assert(std::is_same_v<int, acpl::value_type_t<const int>>);
    

    For the record, this is my first try:

    template<typename T>
    struct value_type {
    };
    
    template<typename T>
    requires(std::is_scalar_v<T>)
    struct value_type<T> {
        using type = T;
    };
    
    template<typename T>
    requires(not std::is_scalar_v<T>)
    struct value_type<T> {
        using type = typename T::value_type;
    };
    
    template<typename T>
    using value_type_t = typename value_type<T>::type;
    
    static_assert(std::is_same_v<int, value_type_t<int>> );
    static_assert(std::is_same_v<int, value_type_t<std::vector<int>>> );
    

    03.02.2019

    C++ Guns: ACPL proudly presents: BinaryHeap

    Filed under: Allgemein — Thomas @ 21:02

    See also
    ACPL: Histogram1D
    ACPL: Histogram2D

    BinaryHeap as array implemente priority queue. Good for way finding graph algorithmen like dijkstra.

    See source code and more code examples at ACPL Binary Heap

    #include <core/util/StringStream.hpp>
    #include <core/util/BinaeryHeap.hpp>
    
    using namespace acpl;
    using ID = int;
    
    int main() {
        // We use a min-heap. The highest priority is the lowest number
        BinaryHeap<double> heap;
        std::map<ID, std::string_view> tasks;
    
        // heap insert returns a ID assiciated with the priority
        tasks.insert({heap.insert(3.0),  "clear drains"});
        tasks.insert({heap.insert(4.0),  "feet cat"});
        tasks.insert({heap.insert(5.0),  "make tea"});
        tasks.insert({heap.insert(1.0),  "solve RC tasks"});
        tasks.insert({heap.insert(2.0),  "tax return"});
        tasks.insert({heap.insert(10.0), "clean room"});
        tasks.insert({heap.insert(99.0), "wash dog"});
    
        std::cout << heap.size() << " tasks" << newline;
    
        std::cout << "Most important: ";
        auto firstToDo = heap.extract_minimum();
        std::cout << tasks[firstToDo.second] << " with priority " << firstToDo.first << newline;
        std::cout << heap.size() << " tasks left" << newline;
    
        std::cout << "\nNext 3 tasks:" << newline;
        for(int i=0; i < 3; i++) {
            auto task = heap.extract_minimum();
            std::cout << tasks[task.second] << " with priority " << task.first << newline;
        }
    
        std::cout <<"make task 'wash the dog' the new first with priority -1" << newline;
        ID lastInsertedID = tasks.rbegin()->first;
        heap.decrease_key(lastInsertedID, -1);
    
        std::cout << "\nThe remaining tasks:" << newline;
        while(not heap.empty()) {
            auto task = heap.extract_minimum();
            std::cout << tasks[task.second] << " with priority " << task.first << newline;
        }
    
        std::cout << heap.size() << " tasks left" << newline;
    }
    
    7 tasks
    Most important: solve RC tasks with priority 1
    6 tasks left
    
    Next 3 tasks:
    tax return with priority 2
    clear drains with priority 3
    feet cat with priority 4
    make task 'wash the dog' the new first with priority -1
    
    The remaining tasks:
    wash dog with priority -1
    make tea with priority 5
    clean room with priority 10
    0 tasks left
    

    C++ Guns: ACPL proudly presents: Histogram2D

    Filed under: Allgemein — Tags: — Thomas @ 21:02

    See also
    ACPL: Histogram1D
    ACPL: BinaryHeap

    Create even 2D Histogram in a fast and intuitive way.
    Define TWO access functions, Axis, Range, Titles, get stochastic moments for every dimension. Enjoy the 2D ASCII art output.
    See source code and more code examples at ACPL Histogram 2D

    std::vector<std::pair<double,double>> values;
    auto temp     = [](const std::pair<double, double>& p){ return p.first; };
    auto pressure = [](const std::pair<double, double>& p){ return p.second; };
    
    HistogramAxis Xaxis("temp [degree]",       FixBinWidth{0.5}, DataIntervalClosed{-4.0,7.0});
    HistogramAxis Yaxis("air pressure [hPa]",  FixBinSize{20},   makeDataIntervalClosed(values, pressure));
    Histogram2D hist = Histogram2D("City temperature VS air pressure",  Xaxis, temp, Yaxis, pressure, values);
    std::cout << hist;
    
    City temperature VS air pressure
    Axis: temp [degree]
    Number of bins: 22 bin width 0.5
    Under/ Overflow count: 4 14
               min    max   avg    var    std 
    Histogram -4 7 3.56703 6.43098 2.53594
    Data      -4.5 7.6 3.62 7.27079 2.69644  
    Axis: air pressure [hPa]
    Number of bins: 20 bin width 1.2
    Under/ Overflow count: 0 0
               min    max   avg    var    std 
    Histogram 1014 1038 1030.45 40.2583 6.34494
    Data      1014 1038 1030.45 40.2583 6.34494
          1038 |
             a |
             i |
             r |
               |
             p |
             r |
             e |
             s |
             s |
             u |
             r |
             e |
               |
             [ |
             h |
             P |
             a |
             ] |
          1014 |
               +-------------------------
                -4                 7
                  temp [degree]
    

    C++ Guns: ACPL proudly presents: Histogram1D

    Filed under: Allgemein — Tags: — Thomas @ 21:02

    See also
    ACPL: Histogram2D
    ACPL: BinaryHeap

    Create 1D Histogram in a fast and intuitive way.
    Define an Axis, Range, Title, get stochastic moments. Enjoy the ASCII art output.
    See source code and more code examples at ACPL Histogram 1D

    std::vector<double> tempValues;
    HistogramAxis axis("temp [degree]", FixBinSize{50}, makeDataIntervalClosed(tempValues));
    Histogram hist = Histogram("City temperature", axis, tempValues);
    cout << hist;
    
    City temperature
    Axis: temp [degree]
    Number of bins: 50 bin width 0.242
    Under/ Overflow count: 0 0
    min max avg var std
    Histogram -4.5 7.6 3.62 7.27079 2.69644
    Data -4.5 7.6 3.62 7.27079 2.69644
    

    30.01.2019

    C++ Guns: C++20 Aggregates can no longer declare constructors (schon wieder anders...)

    Filed under: Allgemein — Tags: — Thomas @ 21:01

    Ab GCC 9.
    P1008 Prohibit aggregates with user-declared constructors
    Initialization in modern C++ - Timur Doumler - Meeting C++ 2018

    Das Thema ist ziemlich lang und eigentlich total überflüssig. Wenn nicht immer die ganzen Altlasten da wären ... but we have to deal with it. Es gibt genügend Artikel im Internet wie gut oder schlecht die Initialisierung in C++ kaputt ist. Für einen guten Überblick, und wie man versucht es zu reparieren, empfehle ich obiges verlinktes PDF.

    struct A {
        A() = delete;
    };
    
    auto func() {
        // ok does not compile
        // A a; 
        // compiles in c++17 but not in 20
        A a{};      // error: use of deleted function 'A::A()'
    }
    

    27.01.2019

    C++ Guns: Stop using std::endl start using acpl::newline and std::clog std::cerr

    Filed under: Allgemein — Tags: — Thomas @ 13:01

    Schaut euch mal C++ Weekly - Ep7 Stop Using std::endl an. Eigentlich hat er ja recht, std::endl wird nicht gebraucht. Problem damit (wie immer), es macht mehr als man vermutet. Denn "end line" suggeriert nicht, dass nun ein Zeilenumbruch kommt und auch kein flush(). Das ganze hat bestimmt wieder historische Gründe.

    Jedenfalls gibt es neben std::cout auch noch std::cerr und std::clog. Die letzten beiden schreiben nicht nach stdout sondern nach stderr. Wobei std::cerr ungebuffert schreibt. Das macht auch Sinn. Fehlermeldungen und Debug Ausgaben müssen sofort raus geschrieben werden, bevor das Programm abstürzt. Die Log Meldungen können erste gepuffert werden um die Performance nicht zu beeinflussen.

    Durch den richtigen Einsatz von std:cout std::cerr und std::clog wird kein std::endl mehr benötigt. Wenn es denn ein normaler Weg gäbe einen Zeilenumbruch zu schreiben...

    Wie lernen denn Programmieranfänger in C++ ein Zeilenumbruch zu schreiben? Natürlich mit std::endl ! Wenn die Leute schon bereit sind, will man sie ja nicht gleich wieder mit ASCII Kontrollzeichen verschrecken. Dabei wäre eine newline() Funktion genau wie die endl() so einfach zu implementieren und standardisieren.

    Bsp:

    template<typename CharT, typename Traits>
    inline std::basic_ostream<CharT, Traits>& newline(std::basic_ostream<CharT, Traits>& os) {
        os << '\n';
        return os;
    }
    
    std::cout << "Test" << newline;
    

    26.01.2019

    Installation von Devuan

    Filed under: Allgemein — Thomas @ 18:01

    Wie ich schon im vorherigen Artikel Installation von FreeBSD schrieb, geht mir Linux auf die Nerven, vorallem der systemd Teil. FreeBSD war allerdings nicht so das Wahre für den Desktop. Für einen Router ist es super klasse, ohne Frage.
    Also ist heute Devuan dran. Das netinstall Image war schon geladen und mit dd auf die Festplatte kopiert. Der alte Rechner bootet ja nicht von USB. Der Devuan Installiert ging auch an und frage mich dann nach einer CDROM :/ Ja, ne, ist nicht. Nach kurzen Suchen fand ich die Möglichkeit manuell den richtigen Path /dev/sdb1 anzugeben.
    Jetzt wird das Basissystem installiert. XFCE und SSH hätte ich genr. Neustart.... hmpf. Aus irgendeinem Grund kann mein neuer Monitor nicht mit den Frequenzen dieser uralten Grafikkarte mithalten.
    Das Problem hatte ich beim FreeBSD auch schon. Das lässt sich auch leicht in der Xorg.conf umstellen

    Und da fängt es schon an zu nerven. GRUB Menu? Neee brauchen wir nicht. Und bis mein Dummer Bildschirm sich wieder einschaltet, ist der längst wieder im X. Im Ausschalten ist der Bildschirm aber schnell ...
    Irgendwie bin ich in eine root Konsole gekommen (Das Hacken mit Hardware Zugriff ist echt einfach)

    SubSection "Display"
        Depth    24
        Modes     "1024x768" "800x600"
    EndSubSection
    

    Und schon ist der Monitor glücklich. Und schon bin ich drin. Devuan XFCE4.

    apt-get install vim gkrellm gcc 

    Was man eben so braucht.

    Youtube in Firefox funktioniert mit 360p ruckelfrei. Da sind die 2 Kerne zu etwas mehr als 50% ausgelastet. Nur die 500MB RAM und die 500MB SWAP sind VOLL. Aber dass Browser Speicher wie dumm fressen ist ja bekannt.
    Den Speicher konnte ich sogar auf 1.5GB aufrüsten. Der Intel Pentium 4 3GHz war doch auch nicht schlecht. Sogar die Grafikkarte lies sich tauschen. Von ATI Rage 128 PRO Ultra AGP 4x mit 32MB auf eine NVIDIA GeForce FX 5700 mit 256MB. Linux Treiber waren überhaupt kein Problem. Alles schon installiert. Auch in 3D!

    Auch den Temperatur und Spannungssensoren funktionieren. Nur die Drehzahl vom Lüfter nicht. Aber das könnte daran liegen, dass der CPU Lüfter kein Tacho Signal hat. Mehr Ventilatoren braucht das Teil nicht.
    Seit Kernel 2.6.xx wurde aber irgendein Sicherheits ACPI Dumm Feature eingebaut, so dass der Treiber w83637hf mit einem Device Busy erst nicht laden wollte. Das ist übrigens auch die Bezeichnung von dem Chip. Das einfügen von

    GRUB_CMDLINE_LINUX="acpi_enforce_resources=lax"
    

    in /etc/default/grub und ein anschließendes update-grub samt reboot lösten das Problem.

    « Newer PostsOlder Posts »

    Powered by WordPress