Dlaczego pisanie do pamięci jest dużo wolniejsze niż czytanie?

Oto prosty memset benchmark przepustowości:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int main()
{
    unsigned long n, r, i;
    unsigned char *p;
    clock_t c0, c1;
    double elapsed;

    n = 1000 * 1000 * 1000; /* GB */
    r = 100; /* repeat */

    p = calloc(n, 1);

    c0 = clock();

    for(i = 0; i < r; ++i) {
        memset(p, (int)i, n);
        printf("%4d/%4ld\r", p[0], r); /* "use" the result */
        fflush(stdout);
    }

    c1 = clock();

    elapsed = (c1 - c0) / (double)CLOCKS_PER_SEC;

    printf("Bandwidth = %6.3f GB/s (Giga = 10^9)\n", (double)n * r / elapsed / 1e9);

    free(p);
}

W moim systemie (szczegóły poniżej) z pojedynczym modułem pamięci DDR3-1600, wyprowadza:

Przepustowość = 4.751 GB / s (Giga = 10^9)

Jest to 37% teoretycznej prędkości pamięci RAM: 1.6 GHz * 8 bytes = 12.8 GB/s

Z drugiej strony, oto podobny test" Czytaj":

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

unsigned long do_xor(const unsigned long* p, unsigned long n)
{
    unsigned long i, x = 0;

    for(i = 0; i < n; ++i)
        x ^= p[i];
    return x;
}

int main()
{
    unsigned long n, r, i;
    unsigned long *p;
    clock_t c0, c1;
    double elapsed;

    n = 1000 * 1000 * 1000; /* GB */
    r = 100; /* repeat */

    p = calloc(n/sizeof(unsigned long), sizeof(unsigned long));

    c0 = clock();

    for(i = 0; i < r; ++i) {
        p[0] = do_xor(p, n / sizeof(unsigned long)); /* "use" the result */
        printf("%4ld/%4ld\r", i, r);
        fflush(stdout);
    }

    c1 = clock();

    elapsed = (c1 - c0) / (double)CLOCKS_PER_SEC;

    printf("Bandwidth = %6.3f GB/s (Giga = 10^9)\n", (double)n * r / elapsed / 1e9);

    free(p);
}

Wychodzi:

Przepustowość = 11.516 GB/s (Giga = 10^9)

Mogę zbliżyć się do teoretyczne ograniczenie wydajności odczytu, takie jak XORing dużej tablicy, ale pisanie wydaje się być znacznie wolniejsze. Dlaczego?

OS Ubuntu 14.04 AMD64 (kompiluję z gcc -O3. Użycie {[6] } sprawia, że wydajność odczytu jest nieco gorsza, ale nie wpływa na memset)

CPU Xeon E5-2630 v2

RAM pojedynczy "16GB PC3-12800 Parity REG CL11 240-Pin DIMM" (co jest napisane na pudełku) myślę, że posiadanie pojedynczego DIMM sprawia, że wydajność jest bardziej przewidywalna. I ' m zakładając, że przy 4 Dimmach, {[3] } będzie do 4 razy szybciej.

Płyta Główna Supermicro X9DRG-QF (obsługuje 4-kanałową pamięć)

System dodatkowy: laptop z 2X 4GB PAMIĘCI RAM DDR3-1067: odczyt i zapis to około 5,5 GB / s, ale należy pamiętać, że używa 2 modułów DIMM.

P. S. zastąpienie memset tą wersją skutkuje dokładnie taką samą wydajnością

void *my_memset(void *s, int c, size_t n)
{
    unsigned long i = 0;
    for(i = 0; i < n; ++i)
        ((char*)s)[i] = (char)c;
    return s;
}
Author: bobcat, 2014-09-13

7 answers

Z Twoimi programami, dostaję

(write) Bandwidth =  6.076 GB/s
(read)  Bandwidth = 10.916 GB/s

Na komputerze stacjonarnym (Core i7, x86-64, GCC 4.9, GNU libc 2.19) z sześcioma modułami DIMM 2GB. (Nie mam więcej szczegółów niż to, przepraszam.)

Jednak ten program zgłasza zapis pasma 12.209 GB/s:

#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <emmintrin.h>

static void
nt_memset(char *buf, unsigned char val, size_t n)
{
    /* this will only work with aligned address and size */
    assert((uintptr_t)buf % sizeof(__m128i) == 0);
    assert(n % sizeof(__m128i) == 0);

    __m128i xval = _mm_set_epi8(val, val, val, val,
                                val, val, val, val,
                                val, val, val, val,
                                val, val, val, val);

    for (__m128i *p = (__m128i*)buf; p < (__m128i*)(buf + n); p++)
        _mm_stream_si128(p, xval);
    _mm_sfence();
}

/* same main() as your write test, except calling nt_memset instead of memset */

Magia jest w _mm_stream_si128, aka Instrukcja maszynowa movntdq, która zapisuje 16-bajtową ilość do pamięci RAM systemu, omijając pamięć podręczną (oficjalnym żargonem na to jest " non-temporal store"). Myślę, że to dość jednoznacznie pokazuje, że różnica wydajności jest Wszystko o zachowaniu pamięci podręcznej.

Uwaga: glibc 2.19 posiada rozbudowaną, ręcznie zoptymalizowaną instrukcję memset wykorzystującą instrukcje wektorowe. Jednak nie używa Nie-czasowych magazynów. Prawdopodobnie jest to właściwe dla memset; ogólnie rzecz biorąc, wyczyścisz pamięć krótko przed jej użyciem, więc chcesz, aby była gorąca w pamięci podręcznej. (Przypuszczam, że nawet cleverer memset może przełączyć się na sklepy nie-temporalne dla naprawdę ogromnego bloku clear, na teorii, że nie możesz chcieć tego wszystkiego w pamięci podręcznej, ponieważ pamięć podręczna po prostu nie jest taka duża.)

Dump of assembler code for function memset:
=> 0x00007ffff7ab9420 <+0>:     movd   %esi,%xmm8
   0x00007ffff7ab9425 <+5>:     mov    %rdi,%rax
   0x00007ffff7ab9428 <+8>:     punpcklbw %xmm8,%xmm8
   0x00007ffff7ab942d <+13>:    punpcklwd %xmm8,%xmm8
   0x00007ffff7ab9432 <+18>:    pshufd $0x0,%xmm8,%xmm8
   0x00007ffff7ab9438 <+24>:    cmp    $0x40,%rdx
   0x00007ffff7ab943c <+28>:    ja     0x7ffff7ab9470 <memset+80>
   0x00007ffff7ab943e <+30>:    cmp    $0x10,%rdx
   0x00007ffff7ab9442 <+34>:    jbe    0x7ffff7ab94e2 <memset+194>
   0x00007ffff7ab9448 <+40>:    cmp    $0x20,%rdx
   0x00007ffff7ab944c <+44>:    movdqu %xmm8,(%rdi)
   0x00007ffff7ab9451 <+49>:    movdqu %xmm8,-0x10(%rdi,%rdx,1)
   0x00007ffff7ab9458 <+56>:    ja     0x7ffff7ab9460 <memset+64>
   0x00007ffff7ab945a <+58>:    repz retq 
   0x00007ffff7ab945c <+60>:    nopl   0x0(%rax)
   0x00007ffff7ab9460 <+64>:    movdqu %xmm8,0x10(%rdi)
   0x00007ffff7ab9466 <+70>:    movdqu %xmm8,-0x20(%rdi,%rdx,1)
   0x00007ffff7ab946d <+77>:    retq   
   0x00007ffff7ab946e <+78>:    xchg   %ax,%ax
   0x00007ffff7ab9470 <+80>:    lea    0x40(%rdi),%rcx
   0x00007ffff7ab9474 <+84>:    movdqu %xmm8,(%rdi)
   0x00007ffff7ab9479 <+89>:    and    $0xffffffffffffffc0,%rcx
   0x00007ffff7ab947d <+93>:    movdqu %xmm8,-0x10(%rdi,%rdx,1)
   0x00007ffff7ab9484 <+100>:   movdqu %xmm8,0x10(%rdi)
   0x00007ffff7ab948a <+106>:   movdqu %xmm8,-0x20(%rdi,%rdx,1)
   0x00007ffff7ab9491 <+113>:   movdqu %xmm8,0x20(%rdi)
   0x00007ffff7ab9497 <+119>:   movdqu %xmm8,-0x30(%rdi,%rdx,1)
   0x00007ffff7ab949e <+126>:   movdqu %xmm8,0x30(%rdi)
   0x00007ffff7ab94a4 <+132>:   movdqu %xmm8,-0x40(%rdi,%rdx,1)
   0x00007ffff7ab94ab <+139>:   add    %rdi,%rdx
   0x00007ffff7ab94ae <+142>:   and    $0xffffffffffffffc0,%rdx
   0x00007ffff7ab94b2 <+146>:   cmp    %rdx,%rcx
   0x00007ffff7ab94b5 <+149>:   je     0x7ffff7ab945a <memset+58>
   0x00007ffff7ab94b7 <+151>:   nopw   0x0(%rax,%rax,1)
   0x00007ffff7ab94c0 <+160>:   movdqa %xmm8,(%rcx)
   0x00007ffff7ab94c5 <+165>:   movdqa %xmm8,0x10(%rcx)
   0x00007ffff7ab94cb <+171>:   movdqa %xmm8,0x20(%rcx)
   0x00007ffff7ab94d1 <+177>:   movdqa %xmm8,0x30(%rcx)
   0x00007ffff7ab94d7 <+183>:   add    $0x40,%rcx
   0x00007ffff7ab94db <+187>:   cmp    %rcx,%rdx
   0x00007ffff7ab94de <+190>:   jne    0x7ffff7ab94c0 <memset+160>
   0x00007ffff7ab94e0 <+192>:   repz retq 
   0x00007ffff7ab94e2 <+194>:   movq   %xmm8,%rcx
   0x00007ffff7ab94e7 <+199>:   test   $0x18,%dl
   0x00007ffff7ab94ea <+202>:   jne    0x7ffff7ab950e <memset+238>
   0x00007ffff7ab94ec <+204>:   test   $0x4,%dl
   0x00007ffff7ab94ef <+207>:   jne    0x7ffff7ab9507 <memset+231>
   0x00007ffff7ab94f1 <+209>:   test   $0x1,%dl
   0x00007ffff7ab94f4 <+212>:   je     0x7ffff7ab94f8 <memset+216>
   0x00007ffff7ab94f6 <+214>:   mov    %cl,(%rdi)
   0x00007ffff7ab94f8 <+216>:   test   $0x2,%dl
   0x00007ffff7ab94fb <+219>:   je     0x7ffff7ab945a <memset+58>
   0x00007ffff7ab9501 <+225>:   mov    %cx,-0x2(%rax,%rdx,1)
   0x00007ffff7ab9506 <+230>:   retq   
   0x00007ffff7ab9507 <+231>:   mov    %ecx,(%rdi)
   0x00007ffff7ab9509 <+233>:   mov    %ecx,-0x4(%rdi,%rdx,1)
   0x00007ffff7ab950d <+237>:   retq   
   0x00007ffff7ab950e <+238>:   mov    %rcx,(%rdi)
   0x00007ffff7ab9511 <+241>:   mov    %rcx,-0x8(%rdi,%rdx,1)
   0x00007ffff7ab9516 <+246>:   retq   

(to jest w libc.so.6, a nie sam program -- druga osoba, która próbowała zrzucić assembly dla memset wydaje się, że tylko znalazła swój wpis PLT. Najprostszym sposobem na uzyskanie zrzutu montażowego dla prawdziwego memset w systemie Unixy jest

$ gdb ./a.out
(gdb) set env LD_BIND_NOW t
(gdb) b main
Breakpoint 1 at [address]
(gdb) r
Breakpoint 1, [address] in main ()
(gdb) disas memset
...

.)

 46
Author: zwol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-14 16:00:55

Główna różnica w wydajności wynika z zasad buforowania twojego komputera / regionu pamięci. Gdy odczytywane są z pamięci, A danych nie ma w pamięci podręcznej, pamięć musi być najpierw pobierana do pamięci podręcznej za pomocą magistrali pamięci, zanim będzie można wykonać jakiekolwiek obliczenia z danymi. Jednak podczas zapisu do pamięci istnieją różne zasady zapisu. Najprawdopodobniej Twój system używa pamięci podręcznej do zapisu (a dokładniej "write allocate"), co oznacza, że podczas zapisu w Miejscu Pamięci tego nie ma w pamięci podręcznej, dane są najpierw pobierane z pamięci podręcznej do pamięci podręcznej, a ostatecznie zapisywane z powrotem do pamięci, gdy dane są eksmitowane z pamięci podręcznej, co oznacza round-trip dla danych i 2x wykorzystanie przepustowości magistrali przy zapisie. Istnieje również zasada buforowania zapisu (lub "No-write allocate"), co ogólnie oznacza, że po zapisie bufora-miss at dane nie są pobierane do bufora, a co powinno dać taką samą wydajność zarówno dla odczytu, jak i zapisu.

 29
Author: JarkkoL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-14 01:35:22

Różnica - przynajmniej na moim komputerze, z procesorem AMD-polega na tym, że program read używa wektorowych operacji. Dekompilacja tych dwóch daje to dla programu do pisania:

0000000000400610 <main>:
  ...
  400628:       e8 73 ff ff ff          callq  4005a0 <clock@plt>
  40062d:       49 89 c4                mov    %rax,%r12
  400630:       89 de                   mov    %ebx,%esi
  400632:       ba 00 ca 9a 3b          mov    $0x3b9aca00,%edx
  400637:       48 89 ef                mov    %rbp,%rdi
  40063a:       e8 71 ff ff ff          callq  4005b0 <memset@plt>
  40063f:       0f b6 55 00             movzbl 0x0(%rbp),%edx
  400643:       b9 64 00 00 00          mov    $0x64,%ecx
  400648:       be 34 08 40 00          mov    $0x400834,%esi
  40064d:       bf 01 00 00 00          mov    $0x1,%edi
  400652:       31 c0                   xor    %eax,%eax
  400654:       48 83 c3 01             add    $0x1,%rbx
  400658:       e8 a3 ff ff ff          callq  400600 <__printf_chk@plt>

Ale to dla programu do czytania:

00000000004005d0 <main>:
  ....
  400609:       e8 62 ff ff ff          callq  400570 <clock@plt>
  40060e:       49 d1 ee                shr    %r14
  400611:       48 89 44 24 18          mov    %rax,0x18(%rsp)
  400616:       4b 8d 04 e7             lea    (%r15,%r12,8),%rax
  40061a:       4b 8d 1c 36             lea    (%r14,%r14,1),%rbx
  40061e:       48 89 44 24 10          mov    %rax,0x10(%rsp)
  400623:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  400628:       4d 85 e4                test   %r12,%r12
  40062b:       0f 84 df 00 00 00       je     400710 <main+0x140>
  400631:       49 8b 17                mov    (%r15),%rdx
  400634:       bf 01 00 00 00          mov    $0x1,%edi
  400639:       48 8b 74 24 10          mov    0x10(%rsp),%rsi
  40063e:       66 0f ef c0             pxor   %xmm0,%xmm0
  400642:       31 c9                   xor    %ecx,%ecx
  400644:       0f 1f 40 00             nopl   0x0(%rax)
  400648:       48 83 c1 01             add    $0x1,%rcx
  40064c:       66 0f ef 06             pxor   (%rsi),%xmm0
  400650:       48 83 c6 10             add    $0x10,%rsi
  400654:       49 39 ce                cmp    %rcx,%r14
  400657:       77 ef                   ja     400648 <main+0x78>
  400659:       66 0f 6f d0             movdqa %xmm0,%xmm2 ;!!!! vectorized magic
  40065d:       48 01 df                add    %rbx,%rdi
  400660:       66 0f 73 da 08          psrldq $0x8,%xmm2
  400665:       66 0f ef c2             pxor   %xmm2,%xmm0
  400669:       66 0f 7f 04 24          movdqa %xmm0,(%rsp)
  40066e:       48 8b 04 24             mov    (%rsp),%rax
  400672:       48 31 d0                xor    %rdx,%rax
  400675:       48 39 dd                cmp    %rbx,%rbp
  400678:       74 04                   je     40067e <main+0xae>
  40067a:       49 33 04 ff             xor    (%r15,%rdi,8),%rax
  40067e:       4c 89 ea                mov    %r13,%rdx
  400681:       49 89 07                mov    %rax,(%r15)
  400684:       b9 64 00 00 00          mov    $0x64,%ecx
  400689:       be 04 0a 40 00          mov    $0x400a04,%esi
  400695:       e8 26 ff ff ff          callq  4005c0 <__printf_chk@plt>
  40068e:       bf 01 00 00 00          mov    $0x1,%edi
  400693:       31 c0                   xor    %eax,%eax

Zauważ również, że Twój "domowy" memset jest właściwie zoptymalizowany do wywołania memset:

00000000004007b0 <my_memset>:
  4007b0:       48 85 d2                test   %rdx,%rdx
  4007b3:       74 1b                   je     4007d0 <my_memset+0x20>
  4007b5:       48 83 ec 08             sub    $0x8,%rsp
  4007b9:       40 0f be f6             movsbl %sil,%esi
  4007bd:       e8 ee fd ff ff          callq  4005b0 <memset@plt>
  4007c2:       48 83 c4 08             add    $0x8,%rsp
  4007c6:       c3                      retq   
  4007c7:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  4007ce:       00 00 
  4007d0:       48 89 f8                mov    %rdi,%rax
  4007d3:       c3                      retq   
  4007d4:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4007db:       00 00 00 
  4007de:       66 90                   xchg   %ax,%ax

Nie mogę znaleźć żadnych odniesień dotyczących tego, czy memset używa wektoryzowanych operacji, demontaż memset@plt jest nieprzydatny tutaj:

00000000004005b0 <memset@plt>:
  4005b0:       ff 25 72 0a 20 00       jmpq   *0x200a72(%rip)        # 601028 <_GLOBAL_OFFSET_TABLE_+0x28>
  4005b6:       68 02 00 00 00          pushq  $0x2
  4005bb:       e9 c0 ff ff ff          jmpq   400580 <_init+0x20>

To pytanie sugeruje, że ponieważ memset jest zaprojektowany do obsługi każdej sprawy, może zabraknąć pewnych optymalizacji.

Ten facet zdecydowanie wydaje się przekonany, że aby skorzystać z instrukcji SIMD, trzeba wykonać własny asembler memset. to pytanie również .

Zrobię zdjęcie w ciemności i domyślam się, że nie używa operacji SIMD, ponieważ nie może stwierdzić, czy będzie działać, czy nie na czymś, co jest wielokrotnością wielkości jednej wektoryzowanej operacji, albo Jest jakiś problem związany z wyrównaniem.

Możemy jednak potwierdzić, że jest to , a nie problem wydajności pamięci podręcznej, sprawdzając za pomocą cachegrind. Program write tworzy:

==19593== D   refs:       6,312,618,768  (80,386 rd   + 6,312,538,382 wr)
==19593== D1  misses:     1,578,132,439  ( 5,350 rd   + 1,578,127,089 wr)
==19593== LLd misses:     1,578,131,849  ( 4,806 rd   + 1,578,127,043 wr)
==19593== D1  miss rate:           24.9% (   6.6%     +          24.9%  )
==19593== LLd miss rate:           24.9% (   5.9%     +          24.9%  )
==19593== 
==19593== LL refs:        1,578,133,467  ( 6,378 rd   + 1,578,127,089 wr)
==19593== LL misses:      1,578,132,871  ( 5,828 rd   + 1,578,127,043 wr) << 
==19593== LL miss rate:             9.0% (   0.0%     +          24.9%  )

A program read produkuje:

==19682== D   refs:       6,312,618,618  (6,250,080,336 rd   + 62,538,282 wr)
==19682== D1  misses:     1,578,132,331  (1,562,505,046 rd   + 15,627,285 wr)
==19682== LLd misses:     1,578,131,740  (1,562,504,500 rd   + 15,627,240 wr)
==19682== D1  miss rate:           24.9% (         24.9%     +       24.9%  )
==19682== LLd miss rate:           24.9% (         24.9%     +       24.9%  )
==19682== 
==19682== LL refs:        1,578,133,357  (1,562,506,072 rd   + 15,627,285 wr)
==19682== LL misses:      1,578,132,760  (1,562,505,520 rd   + 15,627,240 wr) <<
==19682== LL miss rate:             4.1% (          4.1%     +       24.9%  )

Podczas gdy program read ma niższą liczbę błędów LL, ponieważ wykonuje o wiele więcej odczytów( dodatkowy odczyt na operację XOR), całkowita liczba błędów wynosi to samo. Więc cokolwiek jest problemem, nie ma go tam.

 16
Author: Patrick Collins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:08:44

Buforowanie i lokalizacja prawie na pewno wyjaśniają większość efektów, które widzisz.

Nie ma buforowania ani lokalizacji w zapisach, chyba że chcesz systemu niedeterministycznego. Większość czasów zapisu jest mierzona jako czas potrzebny na dane, aby dostać się do nośnika pamięci (niezależnie od tego, czy jest to dysk twardy, czy układ pamięci), podczas gdy odczyty mogą pochodzić z dowolnej liczby warstw pamięci podręcznej, które są szybsze niż nośnik pamięci.

 9
Author: Robert Harvey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-13 23:45:11

Może być tak, jak działa (System-jako-całość). Szybkość odczytu wydaje się być powszechnym trendem z szerokim zakresem względnej wydajności przepustowej. Na szybka analiza DDR3 Intel i wykresy DDR2 wymienione, jako kilka wybranych przypadków (zapis/odczyt)%;

Niektóre wydajne układy DDR3 zapisują około ~60-70% przepustowości odczytu. Istnieją jednak moduły pamięci (np. Golden Empire CL11-13-13 D3-2666) ~ 30% pisz

Wydajne układy DDR2 wydają się mieć tylko około ~50% przepustowości zapisu w porównaniu do odczytu. Ale są też szczególnie źli pretendenci (tj. OCZ OCZ21066NEW_BT1G) do ~20%.

Chociaż może to nie wyjaśniać przyczyny zapisu/odczytu ~40%, ponieważ użyty kod benchmarka i konfiguracja są prawdopodobnie różne (notatki są niejasne), jest to zdecydowanie A czynnik. (Chciałbym uruchomić kilka istniejących programów benchmarkowych i sprawdzić, czy liczby są zgodne z kodami zamieszczonymi w pytaniu.)


Aktualizacja:

Pobrałem tabelę wyszukiwania pamięci z połączonej strony i przetworzyłem ją w Excelu. Chociaż nadal pokazuje szeroki zakres wartości, jest znacznie mniej przecinany niż oryginalna odpowiedź powyżej, która spojrzała tylko na chipy pamięci odczytane z góry i kilka wybranych "interesujących" wpisów z Wykresów. Nie jestem pewien, po co te rozbieżności, zwłaszcza w strasznych pretendentach wymienionych powyżej, nie występują na liście drugorzędnej.

Jednak, nawet pod nowymi liczbami różnica nadal waha się od 50% -100% (mediana 65, średnia 65) wydajności odczytu. Należy pamiętać, że tylko dlatego, że chip był" 100% " sprawny w stosunku zapisu/odczytu nie oznacza, że był lepszy ogólnie .. tylko, że była bardziej wyrównana między obiema operacjami.

 6
Author: user2864740,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-14 23:32:58

Oto moja hipoteza robocza. Jeśli jest poprawny, wyjaśnia, dlaczego pisze się dwa razy wolniej niż czyta:

Mimo że memset zapisuje tylko do pamięci wirtualnej, ignorując jej poprzednią zawartość, na poziomie sprzętowym komputer nie może wykonać czystego zapisu do pamięci DRAM: odczytuje zawartość pamięci DRAM do pamięci podręcznej, modyfikuje je tam, a następnie zapisuje z powrotem do pamięci DRAM. Dlatego, na poziomie sprzętowym, memset robi zarówno czytanie, jak i pisanie (chociaż pierwsze wydaje się bezużyteczne)! Stąd mniej więcej Dwukrotna różnica prędkości.

 4
Author: bobcat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-14 00:48:02

Ponieważ aby odczytać, wystarczy pulsować linie adresowe i odczytywać Stany rdzenia na liniach zmysłów. Cykl zapisu następuje po dostarczeniu danych do procesora, a tym samym nie spowalnia procesu. Z drugiej strony, aby pisać, musisz najpierw wykonać fałszywy odczyt, aby zresetować rdzenie, a następnie wykonać cykl zapisu.

(Na wszelki wypadek, gdyby nie było to oczywiste, ta odpowiedź brzmi "język w policzku" - opisując, dlaczego zapis jest wolniejszy niż odczyt na Starym pudełku pamięci rdzenia.)

 2
Author: Hot Licks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-15 11:39:45