File size is updated correctly according to 

> fdatasync() is similar to fsync(), but does not flush modified metadata unless that metadata is needed in order to allow a subsequent data retrieval to be correctly handled.  For example, changes to  st_atime  or  st_mtime  (respec‐
       tively,  time  of  last  access  and  time  of last modification; see inode(7)) do not require flushing because they are not necessary for a subsequent data read to be handled correctly.  On the other hand, a change to the file size
       (st_size, as made by say ftruncate(2)), would require a metadata flush.

And we don't care about modification times.
Should work alright.
min_offset = parse<UInt64>(argv[3]);
    max_offset = parse<UInt64>(argv[4]);
    block_size = parse<UInt64>(argv[5]);
    descriptors = parse<UInt64>(argv[6]);
    count = parse<UInt64>(argv[7]);
    if (!strcmp(argv[2], "r"))
        mode = MODE_READ;
    else if (!strcmp(argv[2], "w"))
        mode = MODE_WRITE;
    else
        throw Poco::Exception("Invalid mode");
    std::vector<int> fds(descriptors);
    for (size_t i = 0; i < descriptors; ++i)
    {
        fds[i] = open(file_name, O_SYNC | ((mode == MODE_READ) ? O_RDONLY : O_WRONLY));
        if (-1 == fds[i])
            throwFromErrno("Cannot open file", ErrorCodes::CANNOT_OPEN_FILE);
    }
    std::vector<char> buf(block_size);
    pcg64 rng(randomSeed());
    Stopwatch watch;
    std::vector<pollfd> polls(descriptors);
    for (size_t i = 0; i < descriptors; ++i)
    {
        polls[i].fd = fds[i];
        polls[i].events = (mode == MODE_READ) ? POLLIN : POLLOUT;
        polls[i].revents = 0;
    }
    size_t ops = 0;
    while (ops < count)