tengo telefono sistema que porque nada memoria llena interna insuficiente habiendo espacio error disponible celular almacenamiento c performance memory hardware

telefono - porque mi celular dice memoria llena si no tengo nada



¿Por qué escribir en la memoria es mucho más lento que leerlo? (7)

Aquí hay una referencia de ancho de banda memset simple:

#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); }

En mi sistema (detalles a continuación) con un solo módulo de memoria DDR3-1600, genera:

Ancho de banda = 4.751 GB / s (Giga = 10 ^ 9)

Esto es el 37% de la velocidad teórica de RAM: 1.6 GHz * 8 bytes = 12.8 GB/s

Por otro lado, aquí hay una prueba similar de "lectura":

#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); }

Emite:

Ancho de banda = 11.516 GB / s (Giga = 10 ^ 9)

Puedo acercarme al límite teórico para el rendimiento de lectura, como XORing una matriz grande, pero la escritura parece ser mucho más lenta. ¿Por qué?

SO Ubuntu 14.04 AMD64 (compilo con gcc -O3 . Usar -O3 -march=native hace que el rendimiento de lectura sea ligeramente peor, pero no afecta a memset )

CPU Xeon E5-2630 v2

RAM Un único "16GB PC3-12800 Parity REG CL11 240-Pin DIMM" (Lo que dice en la caja) Creo que tener un solo DIMM hace que el rendimiento sea más predecible. Supongo que con 4 DIMM, memset será hasta 4 veces más rápido.

Placa base Supermicro X9DRG-QF (Admite memoria de 4 canales)

Sistema adicional : una computadora portátil con 2x 4 GB de memoria RAM DDR3-1067: la lectura y la escritura son de aproximadamente 5,5 GB / s, pero tenga en cuenta que utiliza 2 módulos DIMM.

PS reemplazar memset con esta versión da como resultado exactamente el mismo rendimiento

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; }


Aquí está mi hipótesis de trabajo. Si es correcto, explica por qué las escrituras son aproximadamente dos veces más lentas que las lecturas:

Aunque memset solo escribe en la memoria virtual, haciendo caso omiso de sus contenidos anteriores, a nivel de hardware, la computadora no puede hacer una escritura pura en DRAM: lee los contenidos de DRAM en caché, los modifica allí y luego los escribe de nuevo en DRAM. Por lo tanto, a nivel de hardware, memset lee y escribe (¡aunque el primero parece inútil)! De ahí la diferencia de velocidad aproximadamente dos veces mayor.


Con tus programas, obtengo

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

en una máquina de escritorio (Core i7, x86-64, GCC 4.9, GNU libc 2.19) con seis DIMM de 2 GB. (No tengo más detalles que eso a mano, lo siento).

Sin embargo, este programa informa ancho de banda de 12.209 GB/s de 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 */

La magia está en _mm_stream_si128 , también conocida como la instrucción de la máquina movntdq , que escribe una cantidad de 16 bytes en la memoria RAM del sistema, evitando el caché (la jerga oficial para esto es " almacenamiento no temporal "). Creo que esto demuestra de manera bastante concluyente que la diferencia en el rendimiento tiene que ver con el comportamiento del caché.

NB glibc 2.19 tiene un memset elaboradamente optimizado a memset que hace uso de instrucciones vectoriales. Sin embargo, no usa tiendas no temporales. Eso es probablemente lo correcto para memset ; en general, borra la memoria poco antes de usarla, por lo que desea que esté caliente en la memoria caché. (Supongo que un memset aún más inteligente podría cambiar a tiendas no temporales por bloques realmente grandes , con la teoría de que no podrías querer todo eso en el caché, porque el caché simplemente no es tan grande).

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

(Esto está en libc.so.6 , no en el programa en sí; la otra persona que intentó volcar el ensamble para memset parece haber encontrado su entrada PLT. La forma más fácil de obtener el volcado de ensamblaje para el memset real en un El sistema Unixy es

$ 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 ...

.)


El almacenamiento en caché y la localidad casi con seguridad explican la mayoría de los efectos que está viendo.

No hay ningún caché o localidad en las escrituras, a menos que desee un sistema no determinista. La mayoría de los tiempos de escritura se miden como el tiempo que tardan los datos en llegar al medio de almacenamiento (ya sea un disco duro o un chip de memoria), mientras que las lecturas pueden provenir de cualquier cantidad de capas de caché que sean más rápidas que el medio de almacenamiento.


La diferencia, al menos en mi máquina, con un procesador AMD, es que el programa de lectura utiliza operaciones vectorizadas. Descompilar los dos rinde esto para el programa de escritura:

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>

Pero esto para el programa de lectura:

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

Además, tenga en cuenta que su memset "de cosecha propia" en realidad está optimizado hasta una llamada a 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

No puedo encontrar ninguna referencia con respecto a si memset usa o no operaciones vectorizadas, el desmontaje de memset@plt es útil aquí:

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>

Esta pregunta sugiere que, dado que memset está diseñado para manejar todos los casos, es posible que falten algunas optimizaciones.

Este chico definitivamente parece convencido de que necesitas enrollar tu propio memset ensamblador para aprovechar las instrucciones SIMD. Esta pregunta también lo hace .

Voy a tomar una foto en la oscuridad y supongo que no está usando operaciones SIMD porque no puede decir si va a operar en algo que es un múltiplo del tamaño de una operación vectorizada, o si hay algo de alineación -asunto relacionado.

Sin embargo, podemos confirmar que no es un problema de eficiencia de caché al verificar con cachegrind . El programa de escritura produce lo siguiente:

==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% )

y el programa de lectura produce:

==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% )

Mientras que el programa de lectura tiene una tasa de errores LL menor porque realiza muchas lecturas más (una lectura extra por operación XOR ), el número total de errores es el mismo. Entonces, sea cual sea el problema, no está allí.


La principal diferencia en el rendimiento proviene de la política de almacenamiento en caché de su PC / región de memoria. Cuando lee desde una memoria y los datos no están en la memoria caché, la memoria debe primero buscarse en la memoria caché a través del bus de memoria antes de poder realizar cualquier cálculo con los datos. Sin embargo, cuando escribe en la memoria hay diferentes políticas de escritura. Lo más probable es que su sistema use caché de escritura no simultánea (o más precisamente "asignación de escritura"), lo que significa que cuando escribe en una ubicación de memoria que no está en la caché, los datos primero se extraen de la memoria y luego se escriben volver a la memoria cuando se desalojan los datos de la memoria caché, lo que significa el viaje de ida y vuelta para los datos y el uso de ancho de banda del bus 2 veces al escribir. También hay una política de caché de escritura directa (o "asignación de no escritura") que generalmente significa que al cache-miss en las escrituras los datos no se captan en la memoria caché, y que deberían acercarse al mismo rendimiento para ambas lecturas y escribe


Podría ser exactamente cómo se lleva a cabo (el-sistema-como-un-todo). La lectura más rápida parece ser una tendencia común con una amplia gama de rendimiento de rendimiento relativo. En un análisis rápido de los gráficos DDR3 Intel y DDR2 listados, como algunos casos seleccionados de (escritura / lectura)% ;

Algunos de los mejores chips DDR3 están escribiendo a aproximadamente ~ 60-70% del rendimiento de lectura. Sin embargo, hay algunos módulos de memoria (es decir, Golden Empire CL11-13-13 D3-2666) hasta solo ~ 30% de escritura.

Los chips DDR2 de mejor rendimiento parecen tener solo aproximadamente ~ 50% del rendimiento de escritura en comparación con la lectura. Pero también hay algunos contendientes notablemente malos (es decir, OCZ OCZ21066NEW_BT1G) hasta ~ 20%.

Si bien esto puede no explicar la causa del ~ 40% de escritura / lectura informado, como el código de referencia y la configuración utilizada es probablemente diferente (las notas son vagas ), este es definitivamente un factor. (Diría algunos programas de referencia existentes y veo si los números coinciden con los del código publicado en la pregunta).

Actualizar:

Descargué la tabla de búsqueda de memoria desde el sitio vinculado y la procesé en Excel. Si bien todavía muestra una amplia gama de valores, es mucho menos grave que la respuesta original anterior, que solo analizó los chips de memoria de lectura superior y algunas entradas "interesantes" seleccionadas de los gráficos. No estoy seguro de por qué las discrepancias, especialmente en los terribles contendientes señalados anteriormente, no están presentes en la lista secundaria.

Sin embargo, incluso con los nuevos números, la diferencia todavía varía ampliamente del 50% al 100% (mediana 65, media 65) del rendimiento de lectura. Tenga en cuenta que el hecho de que un chip sea "100%" eficiente en una relación de escritura / lectura no significa que sea mejor en general ... solo que fue más equilibrado entre las dos operaciones.


Porque para leer, simplemente pulsa las líneas de dirección y lee los estados centrales en las líneas de detección. El ciclo de recuperación se produce después de que los datos se entregan a la CPU y, por lo tanto, no disminuyen la velocidad. Por otro lado, para escribir primero debe realizar una lectura falsa para restablecer los núcleos, luego realice el ciclo de escritura.

(En caso de que no sea obvio, esta respuesta es irónica: describe por qué escribir es más lento que leer en una vieja caja de memoria central).