2017-07-07 8 views
1

Ich benutze Aio, um mehrere Dateien auf verschiedenen Festplatten in einem Thread zu schreiben. Wenn ich gepuffertes Schreiben verwende, ist die IO-Verarbeitung gleichzeitig. Aber CPU-Lasten sind sehr hoch. Wenn ich Dateien mit dem DIRECT-Flag öffne, ist die IO-Verarbeitung nicht gleichzeitig.Wie schreibe ich mehrere Dateien auf verschiedenen Festplatten gleichzeitig in einem Thread mit DMA?

Wie in mehrere Dateien auf verschiedenen Festplatten gleichzeitig in einem Thread mit DMA zu schreiben?

#include <malloc.h> 
#include <stdio.h> 
#include <string.h> 
#include <iostream> 
#include <sstream> 
#include <inttypes.h> 

#include <unistd.h> 
#include <fcntl.h> 
#include <sys/syscall.h> 
#include <linux/aio_abi.h> 

using namespace std; 

long double timeDiff(timespec start, timespec end) { 
const long double s = start.tv_sec + start.tv_nsec * 1.0e-9; 
const long double e = end.tv_sec + end.tv_nsec * 1.0e-9; 
return e - s; 
} 

// nr: maximum number of requests that can simultaneously reside in the context. 
inline int io_setup(unsigned nr, aio_context_t *ctxp) { 
return syscall(__NR_io_setup, nr, ctxp); 
} 

inline int io_destroy(aio_context_t ctx) { 
return syscall(__NR_io_destroy, ctx); 
} 

// Every I/O request that is submitted to 
inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) { 
return syscall(__NR_io_submit, ctx, nr, iocbpp); 
} 

// For every completed I/O request kernel creates an io_event structure. 
// minimal number of events one wants to get. 
// maximum number of events one wants to get. 
inline int io_getevents(aio_context_t ctx, long min_nr, long max_nr, 
    struct io_event *events, struct timespec *timeout) { 
return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout); 
} 

int main(int argc, char *argv[]) { 

// prepare data 
const unsigned int kAlignment = 4096; 
const long data_size = 1600 * 1024 * 12/8; 
//const long data_size = 2448 * 1344 * 12/8; 
void * data = memalign(kAlignment, data_size); 
memset(data, 0, data_size); 
//for (int i = 0; i < data_size; ++i) 
// data[i] = 'A'; 

// prepare fd 
//const int file_num = 3; 
const int file_num = 2; 
int fd_arr[file_num]; 
for (int i = 0; i < file_num; ++i) { 
    ostringstream filename; 
    if (i == 0) { 
     //filename << "/data/test"; 
     filename << "/test"; 
    } else { 
     filename << "/data" << i << "/test"; 
    } 
    //filename << "/data/test" << i; 
    int fd = open(filename.str().c_str(), O_WRONLY | O_NONBLOCK | O_CREAT | O_DIRECT | O_APPEND, 0644); 
    //int fd = open(filename.str().c_str(), O_WRONLY | O_NONBLOCK | O_CREAT | O_DIRECT, 0644); 
    //int fd = open(filename.str().c_str(), O_WRONLY | O_NONBLOCK | O_CREAT, 0644); 
    if (fd < 0) { 
     perror("open"); 
     return -1; 
    } 
    fd_arr[i] = fd; 
} 

aio_context_t ctx; 
struct io_event events[file_num]; 
int ret; 
ctx = 0; 

ret = io_setup(1000, &ctx); 
if (ret < 0) { 
    perror("io_setup"); 
    return -1; 
} 

struct iocb cbs[file_num]; 
for (int i = 0; i < file_num; ++i) { 
    memset(&cbs[i], 0, sizeof(cbs[i])); 
} 
struct iocb * cbs_pointer[file_num]; 
for (int i = 0; i < file_num; ++i) { 
    /* setup I/O control block */ 
    cbs_pointer[i] = &cbs[i]; 
    cbs[i].aio_fildes = fd_arr[i]; 
    cbs[i].aio_lio_opcode = IOCB_CMD_PWRITE; // IOCV_CMD 
    cbs[i].aio_nbytes = data_size; 
} 

timespec tStart, tCurr; 
clock_gettime(CLOCK_REALTIME, &tStart); 

const int frame_num = 10000; 
for (int k = 0; k < frame_num; ++k) { 

    for (int i = 0; i < file_num; ++i) { 
     /* setup I/O control block */ 
     cbs[i].aio_buf = (uint64_t)data; 
     //cbs[i].aio_offset = k * data_size; 
    } 

    ret = io_submit(ctx, file_num, cbs_pointer); 
    if (ret < 0) { 
     perror("io_submit"); 
     return -1; 
    } 

    /* get reply */ 
    ret = io_getevents(ctx, file_num, file_num, events, NULL); 
    //printf("events: %d, k: %d\n", ret, k); 
} 

clock_gettime(CLOCK_REALTIME, &tCurr); 
cout << "frame: " << frame_num << " time: " << timeDiff(tStart, tCurr) << endl; 

ret = io_destroy(ctx); 
if (ret < 0) { 
    perror("io_destroy"); 
    return -1; 
} 

// close fd 
for (int i = 0; i < file_num; ++i) { 
    fsync(fd_arr[i]); 
    close(fd_arr[i]); 
} 
return 0; 
} 

Antwort

1

Linux kann Schreibvorgänge tatsächlich asynchron machen, wenn und nur wenn die physischen Extents, die geschrieben werden, bereits auf der Platte zugeordnet sind. Ansonsten muss es einen Mutex nehmen und zuerst die Zuordnung vornehmen, damit alles synchron wird.

Beachten Sie, dass das Abschneiden der Datei auf eine neue Länge die zugrunde liegenden Extents normalerweise nicht zuordnet. Sie müssen den Inhalt vorab schreiben. Danach umschreiben die gleichen Grenzen werden nun async gemacht und damit gleichzeitig werden.

Wie Sie vielleicht sammeln, Async-Datei-I/O auf Linux ist nicht gut, obwohl es mit der Zeit immer besser wird. Windows oder FreeBSD haben weit überlegene Implementierungen. Selbst OS X ist nicht schrecklich. Verwenden Sie stattdessen einen von diesen.

+0

Vielen Dank! Was ist der Unterschied zwischen dem Öffnen der Datei mit dem O_DIRECT-Flag? Ohne sie ist die Gesamtbandbreite größer als die Schreibbandbreite einer Festplatte, aber die CPU-Auslastung ist hoch. Damit entspricht die Gesamtbandbreite der Bandbreite einer Festplatte. – ceys

+0

Ohne 'O_DIRECT' gehen alle I/O zuerst zum Kernel-Seiten-Cache, d. H.' Memcpy() ', und dann wird sie zu einem späteren Zeitpunkt DMA auf Disk übertragen. Wenn "O_DIRECT" eingeschaltet ist, wenn der Block, den Sie schreiben, ein 4Kb-Vielfaches an einer 4Kb-Grenze ausgerichtet ist und Sie während des I/O ** keinen Zugriff auf diesen Speicher haben, sind die Chancen gut, dass er direkt ohne ein DMA gesendet wird eingreifendes 'memcpy()'. Natürlich, wenn Sie neue Extents zuweisen, wie ich bereits erwähnte, ist dies ein globaler Mutex, so dass Sie serialisieren und Nebenläufigkeit verlieren. –

Verwandte Themen