Multithreading, Speicher ans OS retournieren.



  • 1. Vlt zieht das Threading einen Rattenschwanz an Abhängigkeiten in das Projekt und bläht den Speicherverbrauch auf.
    2. Möglicherweise musste auch einfach nur der Heap wachsen und macht das in großen Schritten, um seltener wachsen zu müssen.

    Mach mal Folgendes: Geh in die Endlosschleife, besorg dir die PID deines Prozesses und mach 'cat /proc/DIE PID/maps'. Bei beiden. Das kannst du hier posten oder selbst interpretieren. 😉



  • Führ halt nen Scope ein, damit das thread-Objekt auch vor der Schleife aufgeräumt wird...



  • 314159265358979 schrieb:

    Führ halt nen Scope ein, damit das thread-Objekt auch vor der Schleife aufgeräumt wird...

    Das sagte ich doch schon... 😉



  • Ethon schrieb:

    1. Vlt zieht das Threading einen Rattenschwanz an Abhängigkeiten in das Projekt und bläht den Speicherverbrauch auf.
    2. Möglicherweise musste auch einfach nur der Heap wachsen und macht das in großen Schritten, um seltener wachsen zu müssen.

    Mach mal Folgendes: Geh in die Endlosschleife, besorg dir die PID deines Prozesses und mach 'cat /proc/DIE PID/maps'. Bei beiden. Das kannst du hier posten oder selbst interpretieren. 😉

    Multithreaded:
    cat /proc/25105/maps
    00400000-00406000 r-xp 00000000 08:23 5767236                            /home/geom/repo/dev/test_all/test_mt2/justATest
    00605000-00606000 r--p 00005000 08:23 5767236                            /home/geom/repo/dev/test_all/test_mt2/justATest
    00606000-00607000 rw-p 00006000 08:23 5767236                            /home/geom/repo/dev/test_all/test_mt2/justATest
    025e6000-02607000 rw-p 00000000 00:00 0                                  [heap]
    7f3b129b4000-7f3b129b5000 ---p 00000000 00:00 0 
    7f3b129b5000-7f3b131b5000 rw-p 00000000 00:00 0 
    7f3b131b5000-7f3b13238000 r-xp 00000000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3b13238000-7f3b13437000 ---p 00083000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3b13437000-7f3b13438000 r--p 00082000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3b13438000-7f3b13439000 rw-p 00083000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3b13439000-7f3b135d0000 r-xp 00000000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f3b135d0000-7f3b137cf000 ---p 00197000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f3b137cf000-7f3b137d3000 r--p 00196000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f3b137d3000-7f3b137d4000 rw-p 0019a000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f3b137d4000-7f3b137da000 rw-p 00000000 00:00 0 
    7f3b137da000-7f3b137ef000 r-xp 00000000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f3b137ef000-7f3b139ee000 ---p 00015000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f3b139ee000-7f3b139ef000 r--p 00014000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f3b139ef000-7f3b139f0000 rw-p 00015000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f3b139f0000-7f3b13ad8000 r-xp 00000000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f3b13ad8000-7f3b13cd8000 ---p 000e8000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f3b13cd8000-7f3b13ce0000 r--p 000e8000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f3b13ce0000-7f3b13ce2000 rw-p 000f0000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f3b13ce2000-7f3b13cf7000 rw-p 00000000 00:00 0 
    7f3b13cf7000-7f3b13d0f000 r-xp 00000000 08:21 406205                     /lib/x86_64-linux-gnu/libpthread-2.13.so
    7f3b13d0f000-7f3b13f0e000 ---p 00018000 08:21 406205                     /lib/x86_64-linux-gnu/libpthread-2.13.so
    7f3b13f0e000-7f3b13f0f000 r--p 00017000 08:21 406205                     /lib/x86_64-linux-gnu/libpthread-2.13.so
    7f3b13f0f000-7f3b13f10000 rw-p 00018000 08:21 406205                     /lib/x86_64-linux-gnu/libpthread-2.13.so
    7f3b13f10000-7f3b13f14000 rw-p 00000000 00:00 0 
    7f3b13f14000-7f3b13f2b000 r-xp 00000000 08:21 941896                     /usr/lib/libboost_thread.so.1.46.1
    7f3b13f2b000-7f3b1412a000 ---p 00017000 08:21 941896                     /usr/lib/libboost_thread.so.1.46.1
    7f3b1412a000-7f3b1412c000 r--p 00016000 08:21 941896                     /usr/lib/libboost_thread.so.1.46.1
    7f3b1412c000-7f3b1412d000 rw-p 00018000 08:21 941896                     /usr/lib/libboost_thread.so.1.46.1
    7f3b1412d000-7f3b1414e000 r-xp 00000000 08:21 406188                     /lib/x86_64-linux-gnu/ld-2.13.so
    7f3b1431c000-7f3b14322000 rw-p 00000000 00:00 0 
    7f3b1434a000-7f3b1434d000 rw-p 00000000 00:00 0 
    7f3b1434d000-7f3b1434e000 r--p 00020000 08:21 406188                     /lib/x86_64-linux-gnu/ld-2.13.so
    7f3b1434e000-7f3b14350000 rw-p 00021000 08:21 406188                     /lib/x86_64-linux-gnu/ld-2.13.so
    7fff08a3a000-7fff08a5b000 rw-p 00000000 00:00 0                          [stack]
    7fff08bff000-7fff08c00000 r-xp 00000000 00:00 0                          [vdso]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
    
    Ohne Multithreading:
    00400000-00403000 r-xp 00000000 08:23 5767236                            /home/geom/repo/dev/test_all/test_mt2/justATest
    00602000-00603000 r--p 00002000 08:23 5767236                            /home/geom/repo/dev/test_all/test_mt2/justATest
    00603000-00604000 rw-p 00003000 08:23 5767236                            /home/geom/repo/dev/test_all/test_mt2/justATest
    01cce000-01cef000 rw-p 00000000 00:00 0                                  [heap]
    7f3385dae000-7f3385e31000 r-xp 00000000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3385e31000-7f3386030000 ---p 00083000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3386030000-7f3386031000 r--p 00082000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3386031000-7f3386032000 rw-p 00083000 08:21 406195                     /lib/x86_64-linux-gnu/libm-2.13.so
    7f3386032000-7f33861c9000 r-xp 00000000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f33861c9000-7f33863c8000 ---p 00197000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f33863c8000-7f33863cc000 r--p 00196000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f33863cc000-7f33863cd000 rw-p 0019a000 08:21 406191                     /lib/x86_64-linux-gnu/libc-2.13.so
    7f33863cd000-7f33863d3000 rw-p 00000000 00:00 0 
    7f33863d3000-7f33863e8000 r-xp 00000000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f33863e8000-7f33865e7000 ---p 00015000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f33865e7000-7f33865e8000 r--p 00014000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f33865e8000-7f33865e9000 rw-p 00015000 08:21 395815                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f33865e9000-7f33866d1000 r-xp 00000000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f33866d1000-7f33868d1000 ---p 000e8000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f33868d1000-7f33868d9000 r--p 000e8000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f33868d9000-7f33868db000 rw-p 000f0000 08:21 924268                     /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
    7f33868db000-7f33868f0000 rw-p 00000000 00:00 0 
    7f33868f0000-7f3386911000 r-xp 00000000 08:21 406188                     /lib/x86_64-linux-gnu/ld-2.13.so
    7f3386ae0000-7f3386ae5000 rw-p 00000000 00:00 0 
    7f3386b0d000-7f3386b10000 rw-p 00000000 00:00 0 
    7f3386b10000-7f3386b11000 r--p 00020000 08:21 406188                     /lib/x86_64-linux-gnu/ld-2.13.so
    7f3386b11000-7f3386b13000 rw-p 00021000 08:21 406188                     /lib/x86_64-linux-gnu/ld-2.13.so
    7fff981ac000-7fff981cd000 rw-p 00000000 00:00 0                          [stack]
    7fff981ff000-7fff98200000 r-xp 00000000 00:00 0                          [vdso]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
    


  • Tachyon schrieb:

    314159265358979 schrieb:

    Führ halt nen Scope ein, damit das thread-Objekt auch vor der Schleife aufgeräumt wird...

    Das sagte ich doch schon... 😉

    OK, gleicher Effekt.

    #include <stdio.h>
    #include <iostream>
    #include <boost/thread.hpp>
    
    using namespace std;
    
    void fun()
    {
            cout<<"Hi, I'm the thread"<<endl;
    };
    
    int main(int ,char**)
    {
    #if 0 
            fun();
    #else
            {
            boost::thread a(fun);
            a.join();
            }
    #endif
    
            cout<<"Thread finished"<<endl;
            while(1);
            return 0;
    }
    


  • Die zusätzlichen 12.5 MB gehen sicher nicht im Thread-Objekt drauf, d.h. ob new() oder nicht wird hier keine Rolle spielen.

    Ich würde eher davon ausgehen dass die Heap-Implementierung den frei gewordenen Speicher nicht ans OS zurückgibt. Entweder weil sie es nicht kann (z.B. wegen Fragmentierung), oder weil sie den Speicher nur dann ans OS zurückgibt, wenn eine bestimmte Grenze überschritten wird. Was für die meisten Applikationen ja auch Sinn macht. Die Heap-Performance würde arg leiden, wenn jede 4K Seite sofort ans OS zurückgegeben würde - dann müsste ja dauernd neuer Speicher angefordert und wieder freigegeben werden.

    Ob es ein echtes Memory-Leak gibt, kann man am einfachsten feststellen, indem man ein geeignetes Tool verwendet. Ich denke Valgrind sollte das können (hab's aber selbst noch nie verwendet, da ich für Windows programmiere und wir BoundsChecker dafür verwenden).

    Nochwas: es ist denkbar, dass eine CRT/SCL Implementierung bestimmte Datenstrukturen die für thread-safety nötig sind erst erzeugt/initialisiert, wenn der erste "nicht-main" Thread bestimmte Funktionen aufruft. Und in Boost.Thread gibt es definitiv einige Datenstrukturen die "lazy" Initialisiert werden, d.h. erst bei Erzeugung des ersten Boost.Thread.

    Ich denke folgendes Testprogramm wäre eher sinnvoll:

    #include <stdio.h>
    #include <iostream>
    #include <boost/thread.hpp>
    
    using namespace std;
    
    void fun()
    {
        cout<<"Hi, I'm the thread"<<endl;
    };
    
    int main(int ,char**)
    {
        cout<<"Hi, I'm main()"<<endl;
    
        {
            boost::thread a(fun);
            a.join();
        }
    
    #if 0
        fun();
    #else
        {
            boost::thread b(fun);
            b.join();
        }
    #endif
    
        cout<<"Thread finished"<<endl;
        while(1);    
        return 0;
    }
    

    Hier dürfte mMn. kein grosser Unterschied mehr bestehen. Wenn doch, dann könnte man wirklich mal auf die Suche gehen ob hier nicht irgendwas verkehrt läuft.



  • um mit "top" den Speicherbedarf

    Nutze valgrind , top bitte nicht



  • hustbaer schrieb:

    Die zusätzlichen 12.5 MB gehen sicher nicht im Thread-Objekt drauf, d.h. ob new() oder nicht wird hier keine Rolle spielen.

    Ich würde eher davon ausgehen dass die Heap-Implementierung den frei gewordenen Speicher nicht ans OS zurückgibt.

    Ich glaube auch, daß der Speicher aus Effizienzgründen nicht gleich retourniert
    wird. Macht nur das Debuggen schwieriger. Valgrind kenne und verwende ich sehr
    gerne. Nur ist meine Anwendung numerisch kritisch, und da nicht alle Rundungs-
    Modi in Valgrind implementiert sind, läuft das nicht. Unser Testprogramm geht
    aber. Endlosschleife entfernt, und dann:

    ==25904== Memcheck, a memory error detector
    ==25904== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
    ==25904== Using Valgrind-3.6.1-Debian and LibVEX; rerun with -h for copyright info
    ==25904== Command: ./justATest
    ==25904== 
    Hi, I'm main()
    Hi, I'm the thread
    Hi, I'm the thread
    Thread finished
    ==25904== 
    ==25904== HEAP SUMMARY:
    ==25904==     in use at exit: 8 bytes in 1 blocks
    ==25904==   total heap usage: 7 allocs, 6 frees, 808 bytes allocated
    ==25904== 
    ==25904== 8 bytes in 1 blocks are still reachable in loss record 1 of 1
    ==25904==    at 0x4C28F9F: malloc (vg_replace_malloc.c:236)
    ==25904==    by 0x4E424C9: boost::detail::get_once_per_thread_epoch() (in /usr/lib/libboost_thread.so.1.46.1)
    ==25904==    by 0x4E3B3BF: ??? (in /usr/lib/libboost_thread.so.1.46.1)
    ==25904==    by 0x4E3B688: boost::detail::get_current_thread_data() (in /usr/lib/libboost_thread.so.1.46.1)
    ==25904==    by 0x4E3CDFA: boost::thread::join() (in /usr/lib/libboost_thread.so.1.46.1)
    ==25904==    by 0x402D4E: main (in /home/geom/repo/dev/test_all/test_mt3/justATest)
    ==25904== 
    ==25904== LEAK SUMMARY:
    ==25904==    definitely lost: 0 bytes in 0 blocks
    ==25904==    indirectly lost: 0 bytes in 0 blocks
    ==25904==      possibly lost: 0 bytes in 0 blocks
    ==25904==    still reachable: 8 bytes in 1 blocks
    ==25904==         suppressed: 0 bytes in 0 blocks
    ==25904== 
    ==25904== For counts of detected and suppressed errors, rerun with: -v
    ==25904== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
    

    Interessant ist, woher die Still-Reachable kommen.



  • Ich glaube auch, daß der Speicher aus Effizienzgründen nicht gleich retourniert wird.

    Ja, weil wenn es gleich danach wieder was vom Programm angefordert wird, fuehlt sich das Betriebssystem verarscht.

    Nur ist meine Anwendung numerisch kritisch, und da nicht alle Rundungs-
    Modi in Valgrind implementiert sind, läuft das nicht.

    Naja, Speicher hat normalerweise nichts mit Rundungen zu tun.



  • knivil schrieb:

    Naja, Speicher hat normalerweise nichts mit Rundungen zu tun.

    Bei Rundungsfehlern werden Prädikate falsch evauliert und das Programm haut
    es in Valgrind auf.





  • memr schrieb:

    Bei Rundungsfehlern werden Prädikate falsch evauliert

    Valgrind fuehrt dein Programm so aus, wie du es kompiliert hast. Klar kann das Verhalten von Debug zu Release unterschiedlich sein, aber das hat mit Valgrind nichts zu tun. Du kannst Valgrind auch mit der Releaseversion benutzen. Du erhaelts immer noch eine Zusammenfassung.

    und das Programm haut es in Valgrind auf.

    Aeh, und das heisst? Bitte auf Deutsch.



  • knivil schrieb:

    memr schrieb:

    Bei Rundungsfehlern werden Prädikate falsch evauliert

    Valgrind fuehrt dein Programm so aus, wie du es kompiliert hast. Klar kann das Verhalten von Debug zu Release unterschiedlich sein, aber das hat mit Valgrind nichts zu tun. Du kannst Valgrind auch mit der Releaseversion benutzen. Du erhaelts immer noch eine Zusammenfassung.

    und das Programm haut es in Valgrind auf.

    Aeh, und das heisst? Bitte auf Deutsch.

    In der rechnerischen Geometrie ist es häufig erforderlich, die Vorzeichen von
    Determinanten zu ermitteln. Beispielsweise, um festzustellen, ob ein Punkt
    oberhalb, unterhalb oder genau in einer Ebene liegt. Ist die Determinante nahe
    Null, so kann wegen der begrenzten Rechengenauigkeit eine falsche Antwort
    berechnet werden, was den Algorithmus in einen falschen Zweig führt ->
    Endlosschleife/Segfault/Assertion. Typischerweise sind viele tausend solcher
    Tests erforderlich, so daß das Problem nicht nur theoretischer Natur ist,
    sondern eigentlich immer schlagend wird. Es gibt Methoden, um die Problematik
    zu handhaben, aber die setzen korrekte Rundung voraus, die in Valgrind nicht
    vorliegt. Deshalb laufen dort solche Programme meist nicht.



  • Ich weiss um die Numerik dahinter Bescheid, nur kann ich den Zusammenhang mit Valgrind nicht herstellen. Valgrind installiert Hooks fuer free und malloc und fuehrt dein Programm aus und tracked deinen Speicher. Dabei kann ich keinen Einfluss auf numerische Berechnungen feststellen. Wo rundet Valgrind?



  • http://stackoverflow.com/questions/1656227/how-does-valgrind-work schrieb:

    Your program is then run on a synthetic CPU provided by the Valgrind core. As new code is executed for the first time, the core hands the code to the selected tool. The tool adds its own instrumentation code to this and hands the result back to the core, which coordinates the continued execution of this instrumented code.

    Das klingt nicht so, als würde valgrind nur einige Hooks einfügen. Deswegen ist valgrind auch so langsam.



  • Gut, ich habe wieder was gelernt. Und liest man weiter, dann kann auch Nulgrind benutzt werden. Sicher gibt es zwischen "simulate every instruction" und "nothing" viele Optionen.



  • knivil schrieb:

    Ich weiss um die Numerik dahinter Bescheid, nur kann ich den Zusammenhang mit Valgrind nicht herstellen. Valgrind installiert Hooks fuer free und malloc und fuehrt dein Programm aus und tracked deinen Speicher. Dabei kann ich keinen Einfluss auf numerische Berechnungen feststellen. Wo rundet Valgrind?

    Naja, etwas mehr tut Valgrind sicher. Nach meinem Verständnis dürfte es
    einen Prozessor simulieren und den Programmcode interpretieren. Anders
    ist die Rundungsdifferenz nicht zu erklären, und die Performance ist ja
    auch deutlich schlechter. Ah, na bitte, hier ist Info zum Thema:

    https://lists-sop.inria.fr/sympa/arc/cgal-discuss/2008-05/msg00151.html
    http://valgrind.org/docs/manual/manual-core.html


Anmelden zum Antworten