Ist eigentlich in Sprachen wie C und C++ auf modernen Betriebssystemen Speicherfragmentierung kein Thema mehr? Wenn ja, wieso nicht? Ich meine jetzt keine Memory-Leaks, sondern es werden viele Objekte unterschiedlicher Größe allokiert und freigeben. Oder sollte man im Code da immer noch gegensteuern (verschiedene Memorypools einrichten usw.)?
lalala schrieb: > verschiedene Memorypools einrichten Sowas machen ordentliche malloc()-Implementierungen von sich aus.
Jörg W. schrieb: > Sowas machen ordentliche malloc()-Implementierungen von sich aus. spätestens beim realloc wird es aber schwer.
Jörg W. schrieb: > Sowas machen ordentliche malloc()-Implementierungen von sich aus. Gut zu wissen. Wie findet man heraus ob eine ordentliche malloc Implementierung vorliegt?
Peter II schrieb: > spätestens beim realloc wird es aber schwer. Nö. Die Implementierungen haben ja durch ihr Pooling in vielen Fällen genug Reserve innerhalb des jeweiligen Pools, um das realloc sofort befriedigen zu können. lalala schrieb: > Wie findet man heraus ob eine ordentliche malloc Implementierung > vorliegt? Sourcecode ansehen.
Jörg W. schrieb: > Sourcecode ansehen. Hä? Welchen, den z.B. von Windows oder den von Visual Studio?
lalala schrieb: > Welchen, den z.B. von Windows oder den von Visual Studio? PGH – Pech gehabt. ;-) Dann kannst du nur hoffen, dass deren Autoren auf der Höhe der Zeit sind und vergleichbar gute Implementierungen liefern wie die, die du dir im Opensource-Umfeld alle ansehen kannst.
Jörg W. schrieb: >> spätestens beim realloc wird es aber schwer. > > Nö. Die Implementierungen haben ja durch ihr Pooling in vielen > Fällen genug Reserve innerhalb des jeweiligen Pools, um das realloc > sofort befriedigen zu können. wenn ich aber immer erst 1 Byte anfordere und dann mit realloc 10Mbyte dann liegt es doch im falschen pool? Oder es muss immer umkopiert werden was aber auch nicht immer vorteilhaft ist.
Jörg W. schrieb: > Dann kannst du nur hoffen, dass deren Autoren auf der Höhe der Zeit > sind und vergleichbar gute Implementierungen liefern wie die, die > du dir im Opensource-Umfeld alle ansehen kannst. Achso, dann sind die guten Implementierungen die die möglichst viel Zeit brauchen. http://locklessinc.com/benchmarks_allocator.shtml
Peter II schrieb: > wenn ich aber immer erst 1 Byte anfordere und dann mit realloc 10Mbyte > dann liegt es doch im falschen pool? Ja. > Oder es muss immer umkopiert werden > was aber auch nicht immer vorteilhaft ist. Was heißt „immer“? Es muss genau in diesem Falle umkopiert werden, und umkopiert werden muss ja auch nur das eine Byte. Ansonsten solltest du dir natürlich mal deine Strategie überdenken. Sonderlich sinnvoll klingt das nicht. Wenn du anfangs noch nicht weißt, was du überhaupt brauchst, dann lass den Pointer einfach erstmal bei NULL, damit kommt realloc() problemlos zurecht (es mutiert dann zum malloc()). Ein Byte „nur mal prophylaktisch“ zu allozieren, hat jedenfalls keinen großen Sinn.
Jörg W. schrieb: > lalala schrieb: >> Welchen, den z.B. von Windows oder den von Visual Studio? > > PGH – Pech gehabt. ;-) > > Dann kannst du nur hoffen, dass deren Autoren auf der Höhe der Zeit > sind und vergleichbar gute Implementierungen liefern wie die, die > du dir im Opensource-Umfeld alle ansehen kannst. Da hab ich so meine Zweifel bei Visual Studio, da das bei C bekanntlich alles andere als auf der Höhe der Zeit ist. Peter II schrieb: > Jörg W. schrieb: >>> spätestens beim realloc wird es aber schwer. >> >> Nö. Die Implementierungen haben ja durch ihr Pooling in vielen >> Fällen genug Reserve innerhalb des jeweiligen Pools, um das realloc >> sofort befriedigen zu können. > > wenn ich aber immer erst 1 Byte anfordere und dann mit realloc 10Mbyte > dann liegt es doch im falschen pool? Oder es muss immer umkopiert werden > was aber auch nicht immer vorteilhaft ist. Das wird es meistens sowieso, denn oft ist ja direkt danach gar nicht genug Platz frei. Und 1 Byte umzukopieren dauert auch nicht so extrem lange. ;-)
Jörg W. schrieb: > Ansonsten solltest du dir natürlich mal deine Strategie überdenken. > Sonderlich sinnvoll klingt das nicht. war ja nur als Beispiel gedacht. Mit einer ungünstigen Programmierung ist auch beim einen guten malloc Speicherfragmentierung nicht verschwunden.
Rolf M. schrieb: > Da hab ich so meine Zweifel bei Visual Studio, da das bei C bekanntlich > alles andere als auf der Höhe der Zeit ist. Andererseits ist eine effiziente Speicherallozierung auch im Sinne von C++, und da sind sie meines Wissens nicht so hinterwäldlerisch wie bei C. Letztlich ist "new" ja weiter nichts als ein Wrapper um malloc() (mit nachfolgendem Aufruf der Konstruktoren natürlich).
lalala schrieb: > Ist eigentlich in Sprachen wie C und C++ auf modernen Betriebssystemen > Speicherfragmentierung kein Thema mehr? Man sollte hier auch die Formulierung "auf modernen Betriebssystemen" in der Frage berücksichtigen. Auf pagenden Systemen sollte selbst eine tatsächlich existente Speicherfragmentierung kein Thema sein. Wenn solche unbenutzten Speicherfragmente entstehen sollten, werden die entsprechenden Memory-Pages einfach irgendwann rausgeswappt. Wenn der virtuell vorhandene Speicherraum wesentlich größer ist als der physikalische, entsteht da auch kein Problem durch Fragmentierung.
Grundsätzlich brauchen malloc (), free () und Konsorten auch Zeit. Allerdings sollte man diese auch relativ sehen. Es besteht ein riesiger Unterschied dazwischen ob in einer Schleife 100000-mal Platz für einen Zeiger reserviert wird oder ob am Anfang der Arbeit Platz für eine Inputdatei geschaffen wird. Noch schlimmer sind die Komfortfunktionen für die Vergrößerung eines einmal reservierten Bereichs. Der zeitliche Aspekt der Reservierungsfunktionen wird aber fast immer unter den Tisch gekehrt, obwohl jedem klar sein sollte, dass das Einbinden eines Speicher(bereich)s in eine Speicherverwaltung aufwändig ist. Auch wenn selbige einen, vom Betriebssystem unabhängigen "Puffer", verwendet. Der Gedanke, dass es kein Problem mit der Fragmentierung des Speichers mehr gibt, kommt daher, dass die wenigsten Programme so speicherintensiv sind, dass das Ganze ein Problem wird. Oft bekommt man auch nichts davon mit, weil das Betriebssystem einfach weiteren Speicher "erzeugt". Das dann, eventuell beginnende Auslagern auf die Platte, geht meist in dem allgemeinen Plattengedöns unter. Ich würde mich an folgendem Leitfaden orientieren. Wird, wie oben gesagt sehr oft ein kleiner Bereich reserviert - und auch wieder freigegeben - so sollte es auch keine Probleme geben. Sollten aber die reservierten Blöcke zwischen 1 und 100000 Bytes variieren, so sieht das ganze schon ganz anders aus. Wie man sehen kann, habe ich nicht allzu viel Vertrauen in die betriebssystemeigene Speicherverwaltung. Bei sehr großen Blöcken sieht das Ganze aber schon anders aus. Da kann sogar die systemeigene Speicherverwaltung eine Art Defragmentierung vornehmen.
Amateur schrieb: > Wie man sehen kann, habe ich nicht allzu viel Vertrauen in die > betriebssystemeigene Speicherverwaltung. Tja, was man nicht versteht, dem traut man nicht? Das ist wie mit printf(): ich wette, dass die meisten derjenigen, die sowas „aus Prinzip“ (also nicht infolge eigener Untersuchungen und Messungen) ablehnen, in aller Regel mit ihren „selbstgestrickten“ Alternativen deutlich ineffizienter sein werden als die regulären Implementierungen. Um letztere hat sich nämlich jeweils jemand recht intensiv Gedanken gemacht.
Jörg W. schrieb: > lalala schrieb: >> verschiedene Memorypools einrichten > > Sowas machen ordentliche malloc()-Implementierungen von sich aus. macht das denn tatsächlich malloc selbst? Ich hätte erwartet, dass dies die Speicherbeschaffung einfach an das OS weiterdelegiert.
Auf PCs ist Speicherfragmentierung in der Tat spätestens seit 64bit kein Problem mehr, weil der Adreßraum so riesig ist. Das hat übrigens nichts mit der Menge an physisch bestücktem Speicher zu tun, das mappt die CPU schon selber. Auf Mikrocontrollern und dann auch noch ohne MMU ist Speicherfragmentierung ein Problem, und deswegen untersagt MISRA ja auch malloc und Konsorten. Typischerweise alloziert man sich seine Sachen einfach statisch und sieht dann schon im Mapfile, ob der Speicher ausreicht. Braucht man wirklich und echt unverzichtbar dynamische Speicherverwaltung auf einem Mikrocontroller, dann ist eine Möglichkeit, die angeforderte Speichermenge beim Allozieren immer zur nächsten Zweierpotenz aufzurunden. Damit entsteht keine Fragmentierung, und es läßt sich trivial nachweisen, daß nie mehr als 50% verschwendet werden. Und printf braucht man allenfalls, wenn man tatsächlich floats auf einem Controller in Ausgabestrings packen muß, was selten ist. Eine Konvertierung von Integer nach ASCII ist hingegen trivial zu schreiben.
@Jörg Wunsch >Amateur schrieb: >> Wie man sehen kann, habe ich nicht allzu viel Vertrauen in die >> betriebssystemeigene Speicherverwaltung. >Tja, was man nicht versteht, dem traut man nicht? Du musst es ja (besser) wissen.
Vlad T. schrieb: > Ich hätte erwartet, dass dies die Speicherbeschaffung einfach an das OS > weiterdelegiert. das OS vergibt immer nur Pages (4k). Der Overhead bis zu kernel ist für einfache Malloc viel zu groß, dann muss schon die runtime vom Prozess machen.
Vlad T. schrieb: > ... Ergänzung: wenn man sich den crt-Code des MSVC anschaut (ja, der ist in jeder Studioauslieferung dabei) da ist es auch tatsächlich so, dass einfach die WinAPI Funktion HeapAlloc gecallt wird.
Vlad T. schrieb: > Jörg W. schrieb: >> lalala schrieb: >>> verschiedene Memorypools einrichten >> >> Sowas machen ordentliche malloc()-Implementierungen von sich aus. > > macht das denn tatsächlich malloc selbst? Nur, dass du mal eine Idee bekommst, was einem bei einer aktuellen malloc()-Implementierung so erwartet:
1 | $ pwd |
2 | /usr/src/contrib/jemalloc/src |
3 | $ wc -l *.c |
4 | 2365 arena.c |
5 | 2 atomic.c |
6 | 142 base.c |
7 | 90 bitmap.c |
8 | 395 chunk.c |
9 | 197 chunk_dss.c |
10 | 210 chunk_mmap.c |
11 | 563 ckh.c |
12 | 1673 ctl.c |
13 | 39 extent.c |
14 | 2 hash.c |
15 | 313 huge.c |
16 | 1873 jemalloc.c |
17 | 2 mb.c |
18 | 160 mutex.c |
19 | 1283 prof.c |
20 | 190 quarantine.c |
21 | 67 rtree.c |
22 | 549 stats.c |
23 | 476 tcache.c |
24 | 107 tsd.c |
25 | 657 util.c |
26 | 11355 total |
Jörg W. schrieb: > Nur, dass du mal eine Idee bekommst, was einem bei einer aktuellen > malloc()-Implementierung so erwartet: was fürn compiler ist das? Ich meine, klar, dass für nen Bare-Metal-Compiler da selbst was gestrickt werden muss, aber warum sollte man unter nem OS das am OS vorbei machen?
Vlad T. schrieb: > Vlad T. schrieb: >> ... > > Ergänzung: > wenn man sich den crt-Code des MSVC anschaut (ja, der ist in jeder > Studioauslieferung dabei) da ist es auch tatsächlich so, dass einfach > die WinAPI Funktion HeapAlloc gecallt wird. wobei HeapAlloc noch zur Heapverwaltung gehört Es geht dann später auf VirtualAlloc
Amateur schrieb: >>Tja, was man nicht versteht, dem traut man nicht? > > Du musst es ja (besser) wissen. Ja. Ich habe schließlich erstens selbst mal ein malloc() geschrieben (in der avr-libc), zweitens mir das angesehen, was andere da so machen. (Die avr-libc-Implementierung macht allerdings kein Pooling, da so ein AVR halt nur wenig RAM hat und man dann zu viel verschwendet.) Ich habe mir auch angesehen, was printf macht, auch mal einen Teil davon selbst geschrieben. Dmitry Xmelkov hat das dann nochmal gründlich überarbeitet. Klar, die eigentliche Konvertierung von ein paar Zahlen ist trivial; das ist sie aber auch im printf(). Der eigentliche Gewinn von printf() ist die Bequemlichkeit des „Rundrums“, also weitere Strings mit ausgeben, Festlegen der konkreten Formatierung. Wenn man wirklich nur ein paar nackte Zahlenkolonnen ausgeben will, braucht man kein printf(), logisch. Aber auch da bieten die Bibliotheken fertige Implementierungen für itoa() etc.
Vlad T. schrieb: > Ich meine, klar, dass für nen Bare-Metal-Compiler da selbst was > gestrickt werden muss, aber warum sollte man unter nem OS das am OS > vorbei machen? weil das OS nur Pages von 4k kennt.
Vlad T. schrieb: > macht das denn tatsächlich malloc selbst? > Ich hätte erwartet, dass dies die Speicherbeschaffung einfach an das OS > weiterdelegiert. Ich kann da jetzt nur für UNIX/Linux sprechen, aber ich nehme an, dass es bei Windows zumindest ähnlich ist: malloc() muss es selbst machen, denn es kann nur Happen per brk() und sbrk() an einem Stück holen. Es könnte auch theoretisch wieder solche Blöcke an das Betriebssystem wieder zurückgeben, aber das ist praktisch unmöglich. ein Beispiel:
1 | ptr1 = malloc (10000000); |
2 | ptr2 = malloc (1); |
3 | free (ptr1); |
Die 10 MB kann malloc nicht wieder an das OS zurückgeben. Dazu müsste es erst nach dem LIFO-Prinzip auch das eine Byte wieder zurückgeben. Das wurde aber von der Applikation (noch) nicht wieder freigegeben. malloc() muss daher selbst intelligent agieren, um die 10 MB wieder effizient zu nutzen. Aber wie ich oben schon sagte: Die virtuelle Speicherverwaltung auf modernen Betriebssystem entschärft das Speicherfragmentierungsproblem erheblich.
:
Bearbeitet durch Moderator
Vlad T. schrieb: > Jörg W. schrieb: >> Nur, dass du mal eine Idee bekommst, was einem bei einer aktuellen >> malloc()-Implementierung so erwartet: > > was fürn compiler ist das? jemalloc (einfach Tante Gugel fragen), in diesem Falle direkt aus dem FreeBSD-Sourcetree (weil ich den einfach parat liegen habe). Das wird in diesem Falle also als OS-eigene Variante bei FreeBSD benutzt.
Peter II schrieb: > wobei HeapAlloc noch zur Heapverwaltung gehört > > Es geht dann später auf VirtualAlloc genau - und das ist der Teil, der sich um die Effiziente zuteilung kümmern sollte und VirtualAlloc ist low lewel und page-basierend. Deshalb die Frage, warum jeder Compiler über die OS-Funktionalität nochmal was drüberstülpen sollte. Oder ist Windows da so eine Ausnahme, dass es eine "high-level" Allokationsfunktion gibt?
Vlad T. schrieb: > Deshalb die Frage, warum jeder Compiler über die OS-Funktionalität > nochmal was drüberstülpen sollte. Du solltest hier „Compiler“ nicht mit „Implementierung“ (im Sinne des C-Standards) durcheinander würfeln. Zu letzterer gehört halt die Standardbibliothek, in der auch malloc() drin ist. Das muss natürlich auf das OS abgestimmt sein, aber an irgendeiner Stelle musst du zwangsläufig die Arbeitsteilung zwischen beiden organisieren. Wenn man für jedes einzelne zu allozierende Byte erst einen Syscall ins OS bemühen muss, wird der Overhead viel zu hoch.
Jörg W. schrieb: > Vlad T. schrieb: >> Jörg W. schrieb: >>> Nur, dass du mal eine Idee bekommst, was einem bei einer aktuellen >>> malloc()-Implementierung so erwartet: >> >> was fürn compiler ist das? > > jemalloc (einfach Tante Gugel fragen), in diesem Falle direkt aus > dem FreeBSD-Sourcetree (weil ich den einfach parat liegen habe). > Das wird in diesem Falle also als OS-eigene Variante bei FreeBSD > benutzt. okay, das os benutzt diese Bibliothek. Wahrscheinlich wird es doch auch diese Funktionalität in form eines System-Calls anbieten, oder nicht? Muss wirklich jedes Tool eine eigene Speicherverwaltung einlinken? Ein OS-Call macht doch viel mehr sinn, Implementierungs-Fehler werden unwahrscheinlicher und bei update für alle Programme beseitigt. Frank M. schrieb: > malloc() muss es selbst machen, denn es kann nur Happen per sbrk() an > einem Stück holen. Das ist doch eine Frage der Systemschnittstelle.
Vlad T. schrieb: > okay, das os benutzt diese Bibliothek. Wahrscheinlich wird es doch auch > diese Funktionalität in form eines System-Calls anbieten, oder nicht? Der Syscall dafür ist klassisch sbrk(). Der ändert nur die Größe des Datensegments. > Muss wirklich jedes Tool eine eigene Speicherverwaltung einlinken? Du hast eine falsche Vorstellung von "OS". Die Standardbibliothek liegt zwar im Userland, aber sie ist natürlich inhärenter Bestandteil des OSes. (Bei den BSDs, anders als bei Linux, auch im gleichen SVN-Repository gepflegt wie der Kernel selbst.) > Ein OS-Call macht doch viel mehr sinn, Viel zu aufwändig.
Vlad T. schrieb: > Ein OS-Call macht doch viel mehr sinn Wie Jörg oben schon schrieb: Wenn ich 100000 mal 1 Byte allokiere, machen diese 100000 OS-Calls überhaupt keinen Sinn. Ganz im Gegenteil: Sie bremsen das Programm und das ganze OS aus. Dagegen sind 100000 malloc-Calls überhaupt kein Problem, wenn dieser daraus lediglich eine Handvoll sbrk()-Calls generiert. > Implementierungs-Fehler werden > unwahrscheinlicher und bei update für alle Programme beseitigt. malloc() und Co sind schon viele Jahrzehnte alt und entsprechend ausgereift. Du musst nicht meinen, dass da noch alle 14 Tage ein Bug auftritt. >> malloc() muss es selbst machen, denn es kann nur Happen per sbrk() an >> einem Stück holen. > Das ist doch eine Frage der Systemschnittstelle. Eben, und die ist standardisiert - zumindest bei unixoiden Systemen. Schau Dir brk() und sbrk() an.
:
Bearbeitet durch Moderator
Jörg W. schrieb: > Du hast eine falsche Vorstellung von "OS". > > Die Standardbibliothek liegt zwar im Userland, aber sie ist natürlich > inhärenter Bestandteil des OSes. (Bei den BSDs, anders als bei Linux, > auch im gleichen SVN-Repository gepflegt wie der Kernel selbst.) Moment, Die C-Standardbibliothek kommt doch vom Compiler und nicht vom OS. GCC hat seine, MSVC hat seine, Intel hat seine, jeder Cross-Compiler hat ne eigene. Was anderes ist die Systemschnittstelle des OS. Bei Windows die WinAPI. Diese teilt sich auf, auf verschiedene libs (kernel32, user32, gdi32, ...). `malloc` bringen die Standardbibliotheken der Compiler mit. Und zumindest im VS 2010 wird der Call direkt weitergeleitet an die WinApi-Funktion HeapAlloc, die Teil des OS, nämlich der kernel32.dll ist, und hoffentlich so effizient, wie irgend möglich arbeitet.
:
Bearbeitet durch User
Vlad T. schrieb: > Und > zumindest im VS 2010 wird der Call direkt weitergeleitet an die > WinApi-Funktion HeapAlloc, die Teil des OS, nämlich der kernel32.dll > ist, und hoffentlich so effizient, wie irgend möglich arbeitet. Okay, das mag bei Windows so sein. Bei unixoiden Systemen ist es anders: malloc() holt sich weiteren Speicher per sbrk() System-Call nur dann, wenn es diesen auch braucht. Im allgemeinen werden da auch größere Happen geholt, als die Applikation gerade als Größe dem libc-malloc()-Call mitgeteilt hat, um die Anzahl von System-Calls zu minimieren. System-Calls kosten Zeit - auch unter Windows. Ich halte daher die Verwaltung von malloc() im User-Space für wesentlich eleganter. Genau wie es sinnvoll ist, Speicher in größeren Blöcken auf Festplatten zu verwalten, ist es auch sinnvoller, RAM block-basiert im User-Space zu verwalten, ohne damit das OS zu belasten.
Frank M. schrieb: > System-Calls kosten Zeit - auch unter Windows. Ich halte daher die > Verwaltung von malloc() im User-Space für wesentlich eleganter. Genau > wie es sinnvoll ist, Speicher in größeren Blöcken auf Festplatten zu > verwalten, ist es auch sinnvoller, RAM block-basiert im User-Space zu > verwalten, ohne damit das OS zu belasten. HeapAlloc dürfte kein Syscall sein.
Vlad T. schrieb: > Moment, > Die C-Standardbibliothek kommt doch vom Compiler und nicht vom OS. Keineswegs zwangsläufig. Ich weiß nicht, wie das bei Windows ist, aber bei den Unixen gehört dieses zum Betriebssystem. Wenn ich auf diesem FreeBSD etwas mal mit dem systemeigenen Clang, ein anderes Mal mit GCC compiliere, wird dennoch das malloc() jeweils aus der Systembibliothek benutzt:
1 | $ cat foo.c |
2 | #include <stdlib.h> |
3 | |
4 | int |
5 | main(void) |
6 | { |
7 | volatile char *foo = malloc(42); |
8 | return foo[0]; |
9 | } |
10 | $ cc -O -o foo foo.c |
11 | $ ldd foo |
12 | foo: |
13 | libc.so.7 => /lib/libc.so.7 (0x80081c000) |
14 | $ gcc48 -O -o foo foo.c |
15 | $ ldd foo |
16 | foo: |
17 | libc.so.7 => /lib/libc.so.7 (0x80081c000) |
> GCC hat seine, MSVC hat seine, Intel hat seine, jeder Cross-Compiler hat > ne eigene. Ein Cross-Compiler muss natürlich zwangsläufig seine eigene Bibliothek mitbringen. Diese kann aber (wenn das Target ein OS hat) wiederum auf das Ziel-OS verweisen. Wenn ich obiges Beispiel auf dem FreeBSD mit mingw32-gcc compiliere, dann sehe ich im Mapfile Verweise auf eine libmsvcrt.a, die ein Wrapper für die entsprechende Windows-Systembibliothek ist. Der Wrapper kostet dabei keine Performance, er dient nur dazu, den Inhalt der Windows-DLLs dem GCC so bekannt zu machen, dass der Linker die entsprechenden Symbole auch einfügen kann. MSVC hat ähnliche Wrapper, die dann eben nicht auf .a, sondern auf .lib enden. > `malloc` bringen die Standardbibliotheken der Compiler mit. Und > zumindest im VS 2010 wird der Call direkt weitergeleitet an die > WinApi-Funktion HeapAlloc, die Teil des OS, nämlich der kernel32.dll > ist, und hoffentlich so effizient, wie irgend möglich arbeitet. Wenn die wirklich die Anforderung jedes einzelnen Bytes als Syscall an das OS schicken, dann wundert mich es nicht mehr so sehr, warum beim Compilieren der gleiche Compiler auf gleicher Hardware unter Windows so viel mehr Zeit braucht als unter Linux oder FreeBSD. :}
:
Bearbeitet durch Moderator
Ich habe schon länger nicht mehr den Bedarf gehabt, nachzusehen, aber zumindest früher hat MS selbstverständlich die Sourcen der mit dem Compiler mitgelieferten Runtimelibraries ebenfalls mit dem Compiler ausgeliefert. (Und jetzt gerade habe ich hier keinen installierten MS-Compiler in Griffweite, das Büro ist aus und zu, Wochenende!)
Unixe haben ihren Systemcompiler (gcc, llvm oder beides), der auch die Standardbibliotheken übersetzt hat, die wiederum die entsprechenden Kernel-Syscalls kennt. Das ist bei Windows (zzgl. der Binärkompatiblität) auch so. Das Trio aus Kernel, Standardbibliothek und Compiler gehört grundsätzlich zusammen.
Rufus Τ. F. schrieb: > Ich habe schon länger nicht mehr den Bedarf gehabt, nachzusehen, aber > zumindest früher hat MS selbstverständlich die Sourcen der mit dem > Compiler mitgelieferten Runtimelibraries ebenfalls mit dem Compiler > ausgeliefert. Interessant; ich habe da vorher noch nie reingesehen, muss ich zugeben.
Frank M. schrieb: > Man sollte hier auch die Formulierung "auf modernen Betriebssystemen" in > der Frage berücksichtigen. Auf pagenden Systemen sollte selbst eine > tatsächlich existente Speicherfragmentierung kein Thema sein. Wenn > solche unbenutzten Speicherfragmente entstehen sollten, werden die > entsprechenden Memory-Pages einfach irgendwann rausgeswappt. Man kann aber nicht Bereiche beliebiger Größe rausswappen. Ich weiß nicht, ob heute auch noch mit größeren Pages gearbeitet wird, aber klassisch ist eine Page typischerweise 4096 Bytes groß. Speicherfragmentierung findet aber nicht in Blockgrößen von 4096 statt. Wenn da jetzt nur ein Byte der Page allokiert ist und oft gebraucht wird, kann die ganze Page nicht ausgelagert werden, auch wenn die restlichen 4095 Bytes ungenutzt sind. Wenn dagegen die ganze Page leer ist, dann braucht sie gar nicht igendwohin gemappt sein, weder auf physischen RAM, noch irgendwo in die Auslagerung, denn wozu sollte man sich den Inhalt einer komplett ungenutzten Page extra auf der Festplatte merken? Nop schrieb: > Auf PCs ist Speicherfragmentierung in der Tat spätestens seit 64bit kein > Problem mehr, weil der Adreßraum so riesig ist. Das hat übrigens nichts > mit der Menge an physisch bestücktem Speicher zu tun, das mappt die CPU > schon selber. Aber eben nicht Byteweise, sondern 4k-Blockweise. Es wird aber selten passieren, dass meine Objekte alle exakt 4k groß sind. Die meisten Objekte werden eher deutlich kleiner sein, und wenn man z.B. an Strings denkt, dann kann auch die Größe stark variieren. > Auf Mikrocontrollern und dann auch noch ohne MMU ist > Speicherfragmentierung ein Problem, und deswegen untersagt MISRA ja auch > malloc und Konsorten. Das ist sicher mit ein Grund, aber da gibt's noch ein paar andere. Vlad T. schrieb: > Deshalb die Frage, warum jeder Compiler über die OS-Funktionalität > nochmal was drüberstülpen sollte. Oder ist Windows da so eine Ausnahme, > dass es eine "high-level" Allokationsfunktion gibt? Nö, die gibt's auch auf anderen Systemen und heißt im Falle von unixoiden Systemen in der Regel malloc(). Vlad T. schrieb: > Jörg W. schrieb: >> Du hast eine falsche Vorstellung von "OS". >> >> Die Standardbibliothek liegt zwar im Userland, aber sie ist natürlich >> inhärenter Bestandteil des OSes. (Bei den BSDs, anders als bei Linux, >> auch im gleichen SVN-Repository gepflegt wie der Kernel selbst.) > > Moment, > Die C-Standardbibliothek kommt doch vom Compiler und nicht vom OS. Sagt wer? Bei gcc ist keine Standardbilbiothek dabei. Unter Linux ist die Standardbibliothek die glibc und Teil der Grundinstallation des Betriebssystems. Ohne die würde kein einziges C-Programm funktionieren, wodurch man das System nicht mal booten könnte - es sei denn, man würde alles statisch linken. > Was anderes ist die Systemschnittstelle des OS. Bei Windows die WinAPI. > Diese teilt sich auf, auf verschiedene libs (kernel32, user32, gdi32, > ...). Nun, das hängt eben stark vom Betriebssystem ab. Unter Unix-Systemen ist das die POSIX-Schnittstelle, die (zumindest im Falle der glibc) mit in der Standardbibliothek steckt und nicht nochmal ein eigenes Layer unter dieser ist.
warum ich frage: mir ist aufgefallen, das wohl einige Implementierungen der stl an für mich unerwarteten Stellen (kleine Mengen) Speicher auf dem Heap allocieren. z.B. gslice. Und da fragt man sich doch ob man sich da Sorgen machen muss.
lalala schrieb: > Jörg W. schrieb: >> Sourcecode ansehen. > > Hä? Welchen, den z.B. von Windows oder den von Visual Studio? Man kann gegen beliebige mallocs linken.
Ich kann's mir einfach nicht länger verkneifen: Pages sind nicht zwingend 4kBytes groß, sondern verfügen über eine individuell eingestellte Größe. Diese ist zwar oftmals auf 4kBytes eingestellt, doch kann das auch gerne mal anders sein. 8kBytes je Page, aber auch andere Werte sind durchaus bei einigen Prozessoren nicht nur üblich, sondern mitunter sogar Standard. Zum Beispiel der DEC Alpha war/ist mit 8kBytes bis 4MBytes je Page so ein Kandidat; typisch waren jedoch 8kBytes. Ebenso verwendet der Intel Itanium 8kBytes/Page. Intel x86/64bit(IA64) Prozessoren verwenden typisch 4kBytes, aber auch 2MBytes, 4MBytes oder 1GByte je Page. ARM Prozessoren verwenden oft 4kBytes/Page, mitunter aber auch mal 64kBytes je Page. Für frühe Intel x86(386/468/Pentium II?) ließen etwas von bis zu 32MBytes je Page einstellen, wenn ich mich recht erinnere. Bei den späteren Modellen der x86(IA32) sind Größen von 4kBytes, 2MBytes oder auch 1GByte je Page möglich, und werden/wurden unter Windows(PAE) teilweise auch so eingesetzt. Es ist eben Einstellungssache! Im übrigen ein Grund, weshalb es wenigstens unter UNIX üblich war, und sicherlich auch heute noch ist, die aktuelle Page-Size aus der Systemkonfiguration zu lesen. (siehe: 'int sysconf(_SC_PAGESIZE);' im Header "unistd.h") Verzichtet man darauf, die konkrete Größe abzufragen, kann man auch gerne mal ziemlich deutlich daneben liegen. Unter Windows wird zum Zweck der Abfrage der Seitengröße übrigens der API-Aufruf 'GetSystemInfo(...);' bzw. 'GetNativeSystemInfo(...);' verwendet. Ist wirklich nicht bös gemeint, aber ich konnte es schon allein aus Passion nicht lassen, diese Anmerkung bezüglich der vermeintlich auf 4kBytes festgelegten PAGESIZE zu verfassen. :-) Beste Grüße aus Berlin, agk.
:
Bearbeitet durch User
Armin G. schrieb: > über eine individuell eingestellte Größe Naja, das klingt jetzt fast so, als könnte man sich diese beliebig wählen. Selbst, wenn es keine feste Größe ist, sind es nur einige wenige verschiedene Werte, die die Pagesize annehmen kann, und Rolfs wesentliches Argument (dass man nur seitenweise überhaupt auslagern kann und nicht ein paar einzelne ungenutzte Bytes) bleibt davon völlig unberührt.
>der stl an für mich unerwarteten Stellen (kleine Mengen) Speicher auf >dem Heap allocieren. z.B. gslice. Und da fragt man sich doch ob man sich >da Sorgen machen muss. In dem Moment wo Du irgendwelche Bibliotheken verwendest, sind solche Effekte "normal". Der ursprüngliche Programmierer stand wohl vor der Wahl: Soll ich Dir fix einen Speicherbereich "klauen" oder soll ich diesen nur bei Benutzung reservieren. Noch interessanter wird es, wenn zur Zeit der Programmierung nicht bekannt ist wieviel denn benötigt wird. Noch ein Problem: Eine Bibliothek kann Jahrelang im Einsatz sein und keine gravierenden Probleme bereiten. Ein kleines Speicherleck, welches aber unerkannt existiert und immer zwischen Deinen eigenen - natürlich fehlerfreien - Speicherreservierungen was abzwackt, kann zu recht interessanten Effekten führen.
Pümpler schrieb: > Interessant; ich habe da vorher noch nie reingesehen, muss ich zugeben. Da gibt es aber kein malloc ... Unter C:\Program Files (x86)\Windows Kits\10 zwar malloc.cpp, aber das hat hiermit zu tun: https://blogs.msdn.microsoft.com/vcblog/2015/03/03/introducing-the-universal-crt/
Jörg W. schrieb: > lalala schrieb: >> Welchen, den z.B. von Windows oder den von Visual Studio? > > PGH – Pech gehabt. ;-) Nö. Nur nicht hingeschaut. Die Quelltexte sind bspw. unter %SystemDrive%\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src zu finden > Dann kannst du nur hoffen, dass deren Autoren auf der Höhe der Zeit > sind und vergleichbar gute Implementierungen liefern wie die, die > du dir im Opensource-Umfeld alle ansehen kannst. Edit: Bevor da jemand weitersucht. Es wird wohl HeapAlloc benutzt https://msdn.microsoft.com/de-de/library/windows/desktop/aa366597(v=vs.85).aspx
:
Bearbeitet durch User
lalala schrieb: > warum ich frage: mir ist aufgefallen, das wohl einige Implementierungen > der stl an für mich unerwarteten Stellen (kleine Mengen) Speicher auf > dem Heap allocieren. z.B. gslice. Und da fragt man sich doch ob man sich > da Sorgen machen muss. Hab grad selbst was mit gslice gemacht. Da mache ich mir eher Sorgen um die Berechnungen, die sich daran anschließen, also um die paar Heap-Anfragen. Nachgemessen habe ich noch nicht, ob es sich auszahlt, den 3-Zeiler auf mehrere geschachteltet Schleifen umzustellen. Da lass ich lieber die CPU heiß laufen als meinen Kopf...
Jörg W. schrieb: > Armin G. schrieb: >> über eine individuell eingestellte Größe > > Naja, das klingt jetzt fast so, als könnte man sich diese beliebig > wählen. > Hallo Jörg W.! Als Anwendungsentwickler wird man dazu sicherlich ehr selten die Gelegenheit haben, da gebe ich dir völlig Recht. In 16bit Emulationen, welche die LocalDescriptorTable mit 64kByte Segmentgrößen verwenden, wäre das zumindest noch denkbar. Doch mitunter möchte man vielleicht auch mal an einem Kernel (Linux/Minix/FORTH etc.) z.B. im Bereich 'Embedded' im Zusammenhang mit Paging herum spielen. Da spielen dann die Einträge in Descriptor-Tables (z.B. Intel GDT/LDT im Atom-Prozessor) schon noch eine Rolle. Und die können auch individuell sehr unterschiedlich sein, sprich es können je nach Prozessorarchitektur unterschiedliche Pagegrößen verwendet werden, aber ebenso auch gemischte Page-Größen existieren ( Intel - Segment Descriptor: GranularityBit[1], SegmentLimit[20] ). Ein Mix aus 4kByte(app) und 4MByte (kernel) Pages ist durchaus denkbar, u.U. sogar sinnvoll. > Selbst, wenn es keine feste Größe ist, sind es nur einige wenige > verschiedene Werte, die die Pagesize annehmen kann, und Rolfs > wesentliches Argument (dass man nur seitenweise überhaupt auslagern > kann und nicht ein paar einzelne ungenutzte Bytes) bleibt davon > völlig unberührt. Naja, ich vermute mal, wenn man Annahmen über vorgegebene Pagegrößen mit in die eigenen Überlegungen mit einbezieht, und diese sich jedoch auch sehr deutlich von den 'vermuteten' Werten unterscheiden können, dann hat das mitunter schon auch noch Einfluss auf das Resourcenmanagement, sowie auch Performanceaspekte. Zudem ist ohne Wissen um die tatsächliche PAGESIZE nicht möglich korrektes Address-Alignment für Speicherallokationen vorzunehmen. Die Abfrage der PAGESIZE kann also durchaus noch sinnvoll sein, besonders im Zusammheng mit ObjectPooling / MemoryPooling. Allokationen von z.B. 48kByte Buffern für eine FFT liegen in einem System mit 64kByte PAGESIZE eben nur bei der ersten Allokation (alignment vorausgesetzt) in ein und derselben Page, die nächste Allokation jedoch liegt ein solcher Buffer ohne Alignment nun überlappend über zwei unterschiedlichen Pages verteilt. Während in einem System mit 4kByte PAGESIZE jede 48kByte Allokationen mehrere Pages überspannt (alignment vorausgesetzt). In der Deallokation oder dem, was tatsächlich ausgelagert wird/werden kann, sind die Verhaltensweisen im Ergebnis nun doch sehr unterschiedlich. Pages, welche durch Objekte mehrfach überspannt werden, können bereits bei regelmäßig erfolgenden Zugriffen auf einen nur kleinen Teil dieser Objekte, entsprechend gar nicht mehr ausgelagert werden ('spanning'-Effekt). Für Applikationen mit exzessivem Speicherverbrauch kann das durchaus Probleme verursachen; da kommt dann u.U. auch mal ein sicherlich heutzutage ehr selten zu sehender OOM-Error aufge-pop-pt. Darüber hinaus können Objekte, welche bestimmte Alignments überspannen, Penalities verursachen. Beispiel: Ein 'double', welches das Alignment einer Cachzeile überspannt, muss durch den Prozessor in mehr als einem einzelnen Zugriff auf den Prozessorcache eingelesen werden. - Anm.: (eigentlich ein blödes Beispiel, denn ein solcher double ist ohnehin schon von Haus aus miss-aligned, nur eben zusätzlich auch noch über die Grenzen einer cacheline hinweg) An dieser Stelle wird deutlich, daß die PAGESIZE gemeinsam mit Alignments für die Performance aufgrund der technischen Gegebenheiten eine durchaus spürbare Rolle spielen können, auch wenn 'moderne' Programmiersprachen mitunter anderes suggerieren. Denn auch wenn ein einzelnes 'miss-aligned object' kaum Unterschiede ausmacht, in der Masse tun sie das sehr wohl; mitunter sogar sehr deutlich. Deshalb war es zumindest 'zu meiner Zeit' noch üblich, bei Speicherintensiven Anwendungen zum einen die tatsächliche PAGESIZE abzufragen, sowie u.U. auch ein Address-Alignment auf PAGESIZE-Boundary bei der Allokation vieler Speicherobjekte vorzunehmen, sprich Pooling zu betreiben, sowie auch Objekte wenn möglich auf technisch/Architektur bedingte Alignments auszurichten (z.B. via '#pragma pack(...)'). Im Einzelfall konnte das auch bedeuten, Objekt-Pools in Vektoren von Datentypen zu zerhackstückeln, um auf diesen Vektoren ohne miss-aligments möglichst performant rechnen zu können. SSE2 hat sogar mal für einige Operationen explizit verlangt, daß die Daten sich an 128bit-boundaries ausrichten. Ob das wohl immer noch so ist? Bei der Gelegenheit: Ich bin ich mir gar nicht so sicher, ob nicht PAGESIZE und die Aligments versus Data-Packing doch viel wichtigere Aspekte sind, als eine mögliche Speicherfragmentierung insgesamt als Problematik darstellt. Ich persönlich würde bei den zunehmend größer werden Arbeitsspeichergrößen (typisch 8-64GByte RAM für Desktop-PCs) jedenfalls die Gefahr der Speicherfragmentierung ehr lax betrachten, mir jedoch durchaus Gedanken über die Alignments im Zusammenhang mit PAGESIZE, CacheLineSize, etc. machen. Denn das hat auch bei tollsten Prozessorgeschwindigkeiten immer noch einen erheblichen Einfluss auf das, was man aus dem Rechner an Leistung auch tatsächlich heraus gequetscht bekommt. Doch ich komme eben ursprünglich aus dem Bereich HPC; deshalb erhebe ich da vorsorglich lieber keinen Anspruch auf allgemein gültige Richtigkeit. Viele Anwendungen kommen sicherlich bereits mit alle dem gut zurecht, was moderne Programmiersprachen und deren Compiler von Haus aus anbieten, ohne sich um irgendwelche Details sorgen zu müssen. Mit besten Grüßen aus Berlin!
:
Bearbeitet durch User
Vlad T. schrieb: > Ich hätte erwartet, dass dies die Speicherbeschaffung einfach an das OS > weiterdelegiert. So war das mal beim Microsoft C-Compiler und Windows 3.1. Nach ein paar Tausend mallocs war dann Ende im Gelände: Out of Handles. Wir mußten unseren eigenen Malloc schreiben. Die nächste Compilerversion brachte dann ihr eigenes Speichermanagement mit, was auch erheblich flotter war als das von Windows.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.