2009-04-20 4 views
12

He hecho esta función en C usando llamadas al sistema (abrir, leer y escribir) para simular la función "cat" en sistemas Linux y es más lenta que la real ...¿Por qué mi función "cat" con llamadas al sistema es más lenta en comparación con "cat" de Linux?

Estoy usando el mismo el tamaño del buffer como el "gato" real y el uso de "strace" creo que está haciendo la misma cantidad de llamadas al sistema. Pero la salida de mi "gato" es un poco más lenta que el "gato" real.

Este es el código que tengo:

#define BUFSIZ 32768 

int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) { 
    ssize_t writtenBytes = 0; 

    while(writtenBytes < readBytes) { 
     writtenBytes += write(fdout, 
      buffer + writtenBytes, readBytes - writtenBytes); 
     if(writtenBytes == -1) { 
      return -1; 
     } 
    } 

    return 0; 
} 

int catPrint(int fdin, int fdout) { 
    char buffer[BUFSIZ]; 
    ssize_t readBytes; 

    do { 
     readBytes = read(fdin, buffer, BUFSIZ); 

     if(readBytes == -1) { 
      return -1; 
     } 

     if(sysWriteBuffer(fdout, buffer, readBytes) == -1) { 
      return -1; 
     } 
    } while(readBytes > 0); 

    return 0; 
} 

estoy leyendo desde un archivo (que me pase como argumento al principal, creo que el código no es necesario aquí) lo que yo llamo el catPrint() funcionar con ese descriptor de archivo y 1 para el descriptor de salida para que se imprima en stdout.

No entiendo por qué es más lento porque estoy usando el mismo archivo para probar y con ambos (el verdadero "gato" y el mío) solo hay una lectura() y una escritura() para todo el texto. ¿No debería el texto completo solo aparecer en la pantalla?

P.S: He etiquetado esto como tarea aunque mi pregunta aquí (por qué es más lenta) no es parte de la tarea. Solo necesitaba usar las llamadas al sistema para crear una función de tipo "cat", que está hecha. Solo estoy intrigado por mi código que es un poco más lento.

problema resuelto con la estupidez de mí
acabo decidió llamar gato original del linux unas cuantas veces en el mismo archivo, uno tras otro, y sólo se dio cuenta de que también era lento algunas de las veces que me lo llamé, tan lento como el mío. Supongo que todo está bien que ...

Perdón por perder el tiempo como esta gente.

+1

En mi humilde opinión, la etiqueta 'homework' es engañosa. Su pregunta se refiere a un hecho de fondo interesante. 'tarea' implica trabajo tedioso de principiantes o (en el otro extremo de la escala) una pregunta de prueba. –

+0

BTW el manejo del error (es decir, escritura que retorna -1) es incorrecto si el error ocurre en la segunda escritura(). – jpalecek

+0

Puedes borrar la etiqueta de tarea si crees que es mejor ... ¿Qué quieres decir con jpalecek? Solo hay una escritura (como en la llamada al sistema) Solo tengo una función auxiliar. Si el write() dentro de esa función auxiliar falla, necesito devolver el -1 hasta donde se llamaba catPrint() ... –

Respuesta

15

Ah, en función de su edición, usted estaba siendo mordido por el buffer readahead. No puede probar dos programas que leen archivos uno al lado del otro ejecutándolos una vez. La primera siempre será más lenta ya que el archivo está en el disco, una vez que el archivo está en la memoria, el segundo se ejecutará más rápido, debe crear nuevos datos para cada uno o ejecutar uno y luego ejecutar ambos para que ambos obtengan el beneficio del buffer readahead.

1

¿Cuánto? El gato canónico es algo así como

char bufr[BUFSIZ]; 
ssize_t len; 

while((len=read(fdin, bufr, BUFSIZ)) >0) 
    write(fdout, bufr, len); 

que guarda algunas instrucciones.

+0

Esta puede ser la versión canónica, pero incorrecta (p. Ej. Si la señal llega mientras escribe()) – jpalecek

+0

¿Qué parte de "algo así como" echó de menos? –

+0

Como dije, el gato original y mi gato, ambos llaman a una lectura() con un tamaño de búfer de 32768 y una escritura() con el mismo tamaño de búfer y una última lectura() al final (cuando no lee nada else y termina). –

3

Quizás compiló sin optimización (o sin una configuración de optimización tan alta)?

Además, su código llamará al sysWriteBuffer una vez con readBytes igual a cero - ¿quizás eso (parcialmente) lo explica?

También puede insertar sysWriteBuffer (ya sea mediante un compilador o manualmente).

"inlining" significa copiar el cuerpo de una función a su sitio de llamada para eliminar la sobrecarga de llamar a una función. Algunas veces los compiladores hacen esto automáticamente (creo que -O3 habilita esta optimización en gcc). También puede usar la palabra clave inline en gcc para indicar al compilador que incorpore una función. Si se hace esto, su declaración se verá así:

static inline int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) { 
.... 
+0

Si usa strace en cat verá que también sucede allí, así que lo dejé ... Y estoy usando la bandera -O2. –

+0

Puedes probar "-O3 -funroll_loops" y ver cómo funciona eso. Mejor aún sería determinar las banderas exactas con las cuales se compiló el gato. –

+0

Solo una nota, la aleta es -funroll-loops (segundo guion no es un guion bajo), y no creo que haga mucho en este caso de todos modos. – Anthony

1

ha efectuado comparaciones strace s de ambos? Puede intentar usar el parámetro -tt para obtener el tiempo de las llamadas de sistema.

+0

Mi conocimiento sobre strace no es mucho y probé el parámetro -tt y apareció un montón de números pero no puedo entender su significado. –

+0

Intente encontrar la parte de lectura y escritura (la salida debe tener el formato "time syscall (parameters) = return value", así que busque read() o write()) y publíquelo – jpalecek

3

Research mmap (2).

Estarás tirando todas las sutilezas de ftell/fread, pero saltará una capa de indirección si el rendimiento de lectura es realmente importante.

+0

Gracias, he necesitado ese mmap ... –

+0

No tengo permitido usar nada más para este ejercicio. –

2

Sin comparar los códigos fuente, es difícil de decir. Si está comparando su gato con el gato GNU, recuerde que está comparando un código que tiene pocas horas/días de antigüedad con un código que evolucionó durante más de veinte años.

Es posible que desee realizar un análisis de rendimiento más completo, ejecutar los dos programas con diferentes tamaños de entrada, desde diferentes dispositivos (un disco RAM sería bueno) y varias veces seguidas. Debe intentar determinar DONDE en su programa es más lento.

Dado que el gato en sí es muy trivial (y usted dijo en un comentario que ya está optimizando la compilación), apuesto a que el impacto en el rendimiento que está observando no está en el algoritmo real, sino en los tiempos de carga del programa. Si el sistema binario es prelinked (que es común en la mayoría de las distribuciones hoy en día), verá que se carga más rápido que cualquier programa que compile (hasta que incluya el pre-enlace de sus programas).

Cuestiones relacionadas