W jaki sposób dane zapisane w pliku naprawdę są przepłukiwane/synchronizowane z urządzeniem blokowym przez Javę.Naprawdę wymuś synchronizację plików/flush w Javie
Próbowałem tego kodu z NIO.
FileOutputStream s = new FileOutputStream(filename)
Channel c = s.getChannel()
while(xyz)
c.write(buffer)
c.force(true)
s.getFD().sync()
c.close()
mam ten c.force (true) togehter z s.getFD sync()() powinny być wystarczające, ponieważ doc dla force stanach
Wymusza aktualizację pliku tego kanału, aby zapisać go na urządzeniu pamięci, które go zawiera. Jeśli plik tego kanału znajduje się na lokalnym urządzeniu pamięci, to po zwróceniu tej metody jest gwarantowane, że wszystkie zmiany dokonane w tym pliku, ponieważ ten kanał został utworzony, lub od czasu ostatniego wywołania tej metody, zostaną zapisane na tym urządzeniu. Jest to przydatne do zapewnienia, że krytyczne informacje nie zostaną utracone w przypadku awarii systemu.
dokumentacja do sync stany:
Siła wszystkie bufory systemowe Aby zsynchronizować z urządzeniem bazowym. Ta metoda zwraca wszystkie zmodyfikowane dane i atrybuty tego FileDescriptor zostały zapisane na odpowiednich urządzeniach. W szczególności, jeśli ten FileDescriptor odnosi się do fizycznego nośnika danych, takiego jak plik w systemie plików, synchronizacja nie powróci, dopóki wszystkie zmodyfikowane w pamięci kopie buforów skojarzonych z tym obiektem FileDesecriptor nie zostaną zapisane na nośniku fizycznym. Synchronizacja ma być używana przez kod, który wymaga fizycznego przechowywania (takiego jak plik), aby był w znanym stanie.
Te dwie rozmowy powinny wystarczyć. Czy to jest? Chyba nie są.
Tło: Wykonuję małe porównanie wydajności (2 GB, zapis sekwencyjny) przy użyciu C/Java, a wersja Java jest dwukrotnie szybsza niż wersja C i prawdopodobnie jest szybsza niż sprzęt (120 MB/s na jednym dysku HD). Próbowałem również wykonać synchronizację narzędzia wiersza poleceń z Runtime.getRuntime(). Exec ("sync"), ale to nie zmieniło zachowania.
Kod C w wyniku czego 70 MB/s (przy użyciu API niskim poziomie (otwarte, pisać, zamknij) nie zmienia się wiele):
FILE* fp = fopen(filename, "w");
while(xyz) {
fwrite(buffer, 1, BLOCK_SIZE, fp);
}
fflush(fp);
fclose(fp);
sync();
Bez ostatecznego wezwania do synchronizacji; Mam nierealistyczne wartości (ponad 1 GB aka pamięci głównej).
Dlaczego między C a Javą jest tak duża różnica? Istnieją dwie możliwości: Nie synchronizuję danych poprawnie w Javie lub kod C jest z jakiegoś powodu nieoptymalny.
Aktualizacja: Wykonałem strace z "strace -cfT cmd". Oto wyniki:
C (Low-Level API): MB/s 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 (High-Level API): MB/s 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, java.io API): MB/s 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 (JRE 1.6 SUN, java.nio API): MB/s 127,45830221558376
5.52 0.980061 490031 2 fsync 1.60 0.284752 9 32774 write 0.00 0.000000 0 80 close
Czas wartości wydają się być tylko czas systemowy i dlatego są całkiem pozbawione sensu.
Aktualizacja 2: Przełączyłem się na inny serwer, zrestartowałem i używam świeżego sformatowanego ext3. Teraz dostaję tylko 4% różnice między Javą i C. Po prostu nie wiem, co poszło nie tak. Czasami rzeczy są dziwne. Powinienem był wypróbować pomiar w innym systemie przed napisaniem tego pytania. Przepraszam.
Update 3: Podsumowując odpowiedź.
- Używaj c.force (true), a następnie s.getFD sync()() Java NIO i s.flush() i s.getFD() .sync() dla API strumienia Java. Dla interfejsu API wysokiego poziomu w C nie zapomnij zsynchronizować. A fflush przesłał dane do systemu operacyjnego, ale nie przyniósł danych do urządzenia blokowego.
- Użyj strace, aby przeanalizować linie systemowe wykonane komendą
- Sprawdź krzyżyk wyników przed opublikowaniem pytania.
Aktualizacja 4: Należy zwrócić uwagę na następującą kontynuację: question.
Chciałbym zobaczyć przepustowość za pomocą tylko funkcji sekcji 2. –
Czego używasz dla BLOCK_SIZE? Czy jest tego samego rozmiaru co twój bufor w Javie? 512 będzie w dzisiejszych czasach bardzo nieoptymalnych. Prawdopodobnie chcesz mieć co najmniej 4096 (rozmiar strony na x86) lub nawet więcej. Na niektórych komputerach zauważyłem wymierne poprawki do 32 tys. Aha, i oczywiście jeśli twój bufor jest wyrównany do strony, to da jądro więcej miejsca na optymalizację. – aij
Innym możliwym problemem jest to, że opublikowany przez Ciebie kod nie używa "API niskiego poziomu (otwórz, napisz, zamknij)". Używa on wyższego poziomu, przenośnego interfejsu API stdio (fopen, fwrite, fclose), który domyślnie dodaje dodatkową warstwę buforowania. Czy jawnie wyłączyłeś buforowanie poza kodem, który opublikowałeś? – aij