Schauen wir uns das etwas genauer an memcpy
und wo wir gerade dabei sind, bei “big O” oder Landau-Notation.
Zuerst, großes O. Wie ich bereits an anderer Stelle erwähnt habe, lohnt es sich, sich an die Definition des großen O zu erinnern, nämlich dass es eine Funktion gibt g(n) wird gesagt, dass O(f(n)) wenn es eine Konstante gibt k wofür g(n) ≤ kf(n). Die Konstante lässt Sie die kleinen Details zugunsten des wichtigen Teils ignorieren. Wie alle bemerkt haben, memcpy
von n Bytes werden An) in fast jeder normalen architektur, denn egal was man zu bewegen hat n Bytes, ein Stück nach dem anderen. Also eine erste, naive Implementierung von memcpy
in C geschrieben werden könnte
unsigned char *
memcpy(unsigned char * s1, unsigned char * s2, long size){
long ix;
for(ix=0; ix < size; ix++)
s1[ix] = s2[ix];
return s1;
}
Dies ist in der Tat An), und vielleicht fragen Sie sich, warum wir uns überhaupt mit einer Bibliotheksroutine beschäftigen. aber die Sache mit dem libc Funktionen ist, dass sie der Ort sind, an dem plattformspezifische Dienstprogramme geschrieben werden; Wenn Sie die Architektur optimieren möchten, ist dies einer der Orte, an denen Sie dies tun können. So, je nach Architektur, gibt es möglicherweise effizientere Implementierungsoptionen; Beispielsweise gibt es in der IBM 360-Architektur a MOVL
Anweisung, die Daten verschiebt, besteht aus großen Blöcken, die einen sehr hochoptimierten Mikrocode verwenden. Anstelle dieser Schleife könnte eine 360-Grad-Implementierung von memcpy stattdessen so aussehen
LR 3,S1 LOAD S1 ADDR in Register 3
LR 4,S2
MOVL 3,4,SIZE
(Übrigens keine Garantie dafür, dass es genau der richtige 360-Code ist, aber er dient zur Veranschaulichung.) Diese Implementierung sieht aus mögen statt tun n Schritte um die Schleife herum, wie es der C-Code getan hat, es führt nur 3 Anweisungen aus.
Was Ja wirklich passiert, aber ist, dass es ausgeführt wird O(n) Mikro Anleitung unter der Decke. Was ist anders zwischen beiden ist die Konstante k; weil der Mikrocode viel schneller ist und weil die Anweisungen nur drei Decodierungsschritte enthalten, ist er es dramatisch schneller als die naive Version, aber es ist immer noch An) — es ist nur die Konstante kleiner.
Und genau das können Sie gut gebrauchen memcpy
— es ist nicht asymptotisch schneller, aber die Implementierung ist so schnell, wie es jemand machen könnte auf dieser besonderen Architektur.
1 – Nein. Sie kopieren jeweils einen Block. Sehen http://www.embedded.com/design/configurable-systems/4024961/Optimizing-Memcpy-improves-speed für eine ziemlich gute Analyse.
2 – Dies ist implementierungsabhängig. Sehen http://www.gnu.org/software/libtool/manual/libc/Changing-Block-Size.html für glibc-Details. “In mehreren Zuweisungsimplementierungen erfordert das Verkleinern eines Blocks manchmal das Kopieren.”
- Es kann vermutet werden, dass memcpy so geschrieben werden könnte, dass es eine große Anzahl von Bits umherbewegen würde. zB Es ist durchaus möglich, die Daten mit SSE-Anweisungen zu kopieren, wenn es vorteilhaft ist.
Wie bereits erwähnt, ist es nicht schneller als O (n), aber Speichersysteme haben häufig eine bevorzugte Blockgröße, und es ist auch möglich, beispielsweise die Größe einer Cache-Zeile auf einmal zu schreiben.
Angenommen, Sie sprechen über glibc, und da Ihre Fragen von der Implementierung abhängig sind, ist es wahrscheinlich am besten, nur die Quelle zu überprüfen:
malloc.c
memcpy.c
So wie ich es gelesen habe, wären die Antworten:
- O(N) — Es gibt keine Möglichkeit, Elemente in besserer als linearer Zeit zu kopieren.
- Gelegentlich werden große Elemente kopiert, wenn realloc() verwendet wird, um sie zu verkleinern.
Der x86 hat spezielle Anweisungen zum Scannen und Abgleichen eines Bytes/Wortes in einem Speicherblock sowie eine, die zum Kopieren eines Speicherblocks verwendet werden kann (es ist immerhin eine CISC-CPU). Viele C-Compiler, die die Inline-Assemblersprache und ein Pragma zum Inlining ganzer Funktionen implementieren, nutzen dies seit vielen Jahren in ihren Bibliotheksfunktionen.
Die für die Speicherkopie verwendeten sind movsb/movsw in Kombination mit der rep-Anweisung.
CMPS/MOVS/SCAS/STOS
REP, REPE, REPNE, REPNZ, REPZ
Setup-Register mit src/trg-Adressen und int-Anzahl und los geht’s.