Как данные, записанные в файл , действительно могут быть сброшены / синхронизированы с блочным устройством с помощью Java.
Я попробовал этот код с NIO:
FileOutputStream s = new FileOutputStream(filename)
Channel c = s.getChannel()
while(xyz)
c.write(buffer)
c.force(true)
s.getFD().sync()
c.close()
Я предположил, что c.force (true) вместе с s.getFD (). Sync () должно быть достаточно, потому что документ для force сообщает
Принудительно записывает любые обновления файла этого канала на устройство хранения, которое его содержит.
Если файл этого канала находится на локальном запоминающем устройстве, то при возврате этого метода гарантируется, что все изменения, внесенные в файл с момента создания этого канала или с момента последнего вызова этого метода, будут записаны на это устройство. Это полезно для гарантии того, что критическая информация не будет потеряна в случае сбоя системы.
Документация к sync гласит:
Принудительно синхронизировать все системные буферы с базовым устройством. Этот метод возвращает после того, как все измененные данные и атрибуты этого FileDescriptor были записаны на соответствующие устройства. В частности, если этот FileDescriptor ссылается на физический носитель данных, такой как файл в файловой системе, синхронизация не вернется, пока все измененные в памяти копии буферов, связанных с этим FileDesecriptor, не будут записаны на физический носитель. sync предназначен для использования кодом, который требует, чтобы физическое хранилище (например, файл) находилось в известном состоянии.
Этих двух вызовов должно быть достаточно. Это? Я думаю, что это не так.
Справочная информация: я делаю небольшое сравнение производительности (2 ГБ, последовательная запись) с использованием C / Java, а версия Java в два раза быстрее, чем версия C, и, вероятно, быстрее, чем аппаратная (120 МБ / с на одном HD) , Я также попытался выполнить синхронизацию инструмента командной строки с Runtime.getRuntime (). Exec ("sync"), но это не изменило поведение.
Код C, обеспечивающий скорость 70 МБ / с: (использование низкоуровневых API (открытие, запись, закрытие) мало что меняет):
FILE* fp = fopen(filename, "w");
while(xyz) {
fwrite(buffer, 1, BLOCK_SIZE, fp);
}
fflush(fp);
fclose(fp);
sync();
без последнего вызова синхронизации; Я получил нереальные значения (более 1 ГБ, или производительность основной памяти).
Почему такая большая разница между C и Java? Есть две возможности: я неправильно синхронизирую данные в Java или код C по какой-то причине неоптимален.
Обновление:
Я выполнил стрейс с помощью "strace -cfT cmd". Вот результаты:
C (API низкого уровня):
МБ / с 67,389782
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
87.21 0.200012 200012 1 fdatasync
11.05 0.025345 1 32772 write
1.74 0.004000 4000 1 sync
C (API высокого уровня):
МБ / с 61,796458
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
73.19 0.144009 144009 1 sync
26.81 0.052739 1 65539 write
Java (1.6 SUN JRE, API java.io):
МБ / с 128,6755466197537
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
80.07 105.387609 3215 32776 write
2.58 3.390060 3201 1059 read
0.62 0.815251 815251 1 fsync
Java (1.6 SUN JRE, API java.nio):
МБ / с 127,45830221558376
5.52 0.980061 490031 2 fsync
1.60 0.284752 9 32774 write
0.00 0.000000 0 80 close
Кажется, что значения времени относятся только к системному времени и поэтому довольно бессмысленны.
Обновление 2:
Я переключился на другой сервер, перезагрузился, и я использую новый отформатированный ext3. Теперь я получаю только 4% разницы между Java и C. Я просто не знаю, что пошло не так. Иногда вещи странные. Я должен был попробовать измерение с другой системой, прежде чем писать этот вопрос. К сожалению.
Обновление 3:
Подведем итоги ответов:
- Используйте c.force (true), а затем s.getFD (). Sync () для Java NIO и s.flush () и s.getFD (). Sync () для потокового API Java. Для API высокого уровня в C не забудьте синхронизировать. Fflush отправил данные в ОС, но не перенес их на блочное устройство.
- Используйте strace для анализа системных вызовов, выполненных командой
- Пересмотрите свои результаты перед отправкой вопроса.
Обновление 4:
Пожалуйста, обратите внимание на следующее продолжение вопрос .