2009-04-08 9 views
29

फ़ाइल फ़ाइल में डेटा कैसे लिखा जा सकता है वास्तव में जावा द्वारा ब्लॉक डिवाइस के साथ फ़्लश/सिंक किया जा सकता है।जावा में फ़ाइल सिंक/फ्लश को वास्तव में मजबूर करें

मैं NIO के साथ इस कोड की कोशिश की:। S.getFD() के साथ

FileOutputStream s = new FileOutputStream(filename) 
Channel c = s.getChannel() 
while(xyz) 
    c.write(buffer) 
c.force(true) 
s.getFD().sync() 
c.close() 

मुझे लगता है कि c.force (सही) माना जाता togehter सिंक() पर्याप्त होना चाहिए क्योंकि force राज्यों

के लिए दस्तावेज़
इस चैनल की फ़ाइल के किसी भी अपडेट को उस स्टोरेज डिवाइस पर लिखा जा सकता है जिसमें यह शामिल है। यदि यह चैनल की फ़ाइल स्थानीय स्टोरेज डिवाइस पर रहता है तो जब यह विधि लौटाती है तो यह गारंटी दी जाती है कि इस चैनल के निर्माण के बाद से फ़ाइल में किए गए सभी परिवर्तन, या चूंकि इस विधि को अंतिम बार बुलाया गया था, तो उस डिवाइस पर लिखा होगा। यह सुनिश्चित करने के लिए उपयोगी है कि सिस्टम क्रैश होने की स्थिति में महत्वपूर्ण जानकारी खो नहीं जाती है।

प्रलेखन sync को कहता है:

फोर्स सभी सिस्टम बफ़र्स अंतर्निहित डिवाइस के साथ सिंक्रनाइज़ करने के लिए। यह विधि सभी संशोधित डेटा के बाद लौटाती है और इस फ़ाइल डिस्क्रिप्टर के गुण प्रासंगिक डिवाइस (नों) में लिखे गए हैं। विशेष रूप से, यदि यह फ़ाइलडिस्क्रिप्टर भौतिक भंडारण माध्यम को संदर्भित करता है, जैसे फ़ाइल सिस्टम में फ़ाइल, सिंक तब तक वापस नहीं आ जाएगा जब तक कि इस फ़ाइल से जुड़े बफर की सभी इन-मेमोरी संशोधित प्रतियां भौतिक माध्यम पर लिखी गई हों। सिंक का उपयोग कोड द्वारा किया जाना है जिसके लिए एक ज्ञात स्थिति में भौतिक भंडारण (जैसे फ़ाइल) की आवश्यकता होती है।

ये दो कॉल पर्याप्त होनी चाहिए। क्या यह? मुझे लगता है कि वे नहीं हैं।

पृष्ठभूमि: मैं सी/जावा का उपयोग करके एक छोटी प्रदर्शन तुलना (2 जीबी, अनुक्रमिक लेखन) करता हूं और जावा संस्करण सी संस्करण के मुकाबले दो गुना तेज है और शायद हार्डवेयर से तेज (एक ही एचडी पर 120 एमबी/एस)। मैंने Runtime.getRuntime()। Exec ("sync") के साथ कमांड लाइन टूल सिंक निष्पादित करने का भी प्रयास किया लेकिन इसने व्यवहार को नहीं बदला है। अंतिम कॉल सिंक करने के लिए बिना

FILE* fp = fopen(filename, "w"); 
while(xyz) { 
    fwrite(buffer, 1, BLOCK_SIZE, fp); 
} 
fflush(fp); 
fclose(fp); 
sync(); 

;:

सी कोड 70 एमबी में जिसके परिणामस्वरूप/s है (कम स्तर एपीआई (का उपयोग कर खुला, लिखने, करीब) ज्यादा परिवर्तन नहीं करता है) मुझे अवास्तविक मूल्य मिले (1 जीबी उर्फ ​​मुख्य मेमोरी प्रदर्शन)।

सी और जावा के बीच इतना बड़ा अंतर क्यों है? दो संभावनाएं हैं: मैं जावा में डेटा को सही तरीके से सिंक नहीं करता हूं या सी कोड कुछ कारणों से उप-स्थानिक है।

अद्यतन: मैंने "स्ट्रेस-सीएफटी सेमीडी" के साथ स्ट्रेस रन किए हैं।

सी (निम्न स्तर एपीआई): 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 

सी (उच्च स्तर एपीआई): यहाँ के परिणाम हैं 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 

जावा (1.6 सन जेआरई, जावा.आईओ एपीआई): एमबी/एस 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 

जावा (1.6 रवि JRE, java.nio एपीआई): 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 

समय मान सिस्टम का समय केवल होने लगते हैं और इसलिए बहुत व्यर्थ कर रहे हैं।

अपडेट 2: मैंने दूसरे सर्वर पर स्विच किया, रीबूट किया, और मैं एक नया स्वरूपित ext3 का उपयोग करता हूं। अब मुझे जावा और सी के बीच केवल 4% अंतर मिलते हैं। मुझे नहीं पता कि क्या गलत हुआ। कभी-कभी चीजें अजीब होती हैं। इस प्रश्न को लिखने से पहले मुझे किसी अन्य प्रणाली के साथ माप की कोशिश करनी चाहिए थी। माफ़ कीजिये।

अद्यतन 3: जवाब संक्षेप में प्रस्तुत करने के लिए:।

  • उपयोग c.force (सही) s.getFD() के बाद जावा NIO और s.flush() और s.getFD के लिए सिंक()() .sync() जावा के स्ट्रीम एपीआई के लिए। सी में उच्च स्तरीय एपीआई के लिए सिंक करना न भूलें। एक एफएफएलश ने ओएस को डेटा जमा किया, लेकिन ब्लॉक डेटा पर आपका डेटा नहीं लाता है।
  • कमांड
  • द्वारा किए गए सिस्कोल का विश्लेषण करने के लिए स्ट्रेस का उपयोग करें, प्रश्न पोस्ट करने से पहले अपने परिणामों की जांच करें।

अद्यतन 4: कृपया निम्नलिखित अनुवर्ती question पर ध्यान दें।

+0

मैं वास्तव में केवल अनुभाग 2 फ़ंक्शंस का उपयोग करके थ्रूपुट देखना चाहता हूं। –

+0

आप BLOCK_SIZE के लिए क्या उपयोग कर रहे हैं? क्या जावा में आपके बफर के समान आकार है? इन दिनों 512 बहुत उपोष्णकटिबंधीय होने जा रहा है। आप शायद कम से कम 4096 (x86 पर पेज आकार) या संभवतः उच्चतर चाहते हैं। मैंने कुछ मशीनों पर 32k तक मापनीय सुधार देखा है। ओह, और निश्चित रूप से यदि आपका बफर पेज-गठबंधन है तो यह कर्नेल को ऑप्टिमाइज़ेशन के लिए और अधिक जगह देगा। – aij

+0

एक और संभावित मुद्दा यह है कि आपके द्वारा पोस्ट किया गया कोड "निम्न स्तर के एपीआई (खुला, लिखना, बंद)" का उपयोग नहीं कर रहा है। यह उच्च स्तर, पोर्टेबल stdio API (fopen, fwrite, fclose) का उपयोग कर रहा है जो डिफ़ॉल्ट रूप से बफरिंग की एक अतिरिक्त परत जोड़ देगा। क्या आपने स्पष्ट रूप से आपके द्वारा पोस्ट किए गए कोड के बाहर कहीं बफरिंग बंद कर दी है? – aij

उत्तर

2

आपको हार्डवेयर और ऑपरेटिंग सिस्टम, विशिष्ट जावा संस्करण के बारे में हमें और बताना होगा। आप इस थ्रूपुट को कैसे माप रहे हैं?

आप सही हैं कि बल/सिंक को भौतिक मीडिया को डेटा को मजबूर करना चाहिए।


यहां कॉपी का एक कच्चा संस्करण है। इंटेल मैक पर जीसीसी 4.0 के साथ संकलित, साफ होना चाहिए।

/* rawcopy -- pure C, system calls only, copy argv[1] to argv[2] */ 

/* This is a test program which simply copies from file to file using 
* only system calls (section 2 of the manual.) 
* 
* Compile: 
* 
*  gcc -Wall -DBUFSIZ=1024 -o rawcopy rawcopy.c 
* 
* If DIRTY is defined, then errors are interpreted with perror(3). 
* This is ifdef'd so that the CLEAN version is free of stdio. For 
* convenience I'm using BUFSIZ from stdio.h; to compile CLEAN just 
* use the value from your stdio.h in place of 1024 above. 
* 
* Compile DIRTY: 
* 
*  gcc -DDIRTY -Wall -o rawcopy rawcopy.c 
* 
*/ 
#include <fcntl.h> 
#include <sys/types.h> 
#include <sys/uio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#if defined(DIRTY) 
# if defined(BUFSIZ) 
#  error "Don't define your own BUFSIZ when DIRTY" 
# endif 
# include <stdio.h> 
# define PERROR perror(argv[0]) 
#else 
# define CLEAN 
# define PERROR 
# if ! defined(BUFSIZ) 
#  error "You must define your own BUFSIZ with -DBUFSIZ=<number>" 
# endif 
#endif 

char * buffer[BUFSIZ];   /* by definition stdio BUFSIZ should 
            be optimal size for read/write */ 

extern int errno ;    /* I/O errors */ 

int main(int argc, char * argv[]) { 
    int fdi, fdo ;    /* Input/output file descriptors */ 
    ssize_t len ;    /* length to read/write */ 
    if(argc != 3){ 
     PERROR; 
     exit(errno); 
    } 

    /* Open the files, returning perror errno as the exit value if fails. */ 
    if((fdi = open(argv[1],O_RDONLY)) == -1){ 
     PERROR; 
     exit(errno); 
    } 
    if((fdo = open(argv[2], O_WRONLY|O_CREAT)) == -1){ 
     PERROR; 
     exit(errno); 
    } 

    /* copy BUFSIZ bytes (or total read on last block) fast as you 
     can. */ 
    while((len = read(fdi, (void *) buffer, BUFSIZ)) > -1){ 
     if(len == -1){ 
      PERROR; 
      exit(errno); 
     } 
     if(write(fdo, (void*)buffer, len) == -1){ 
      PERROR; 
      exit(errno); 
     } 
    } 
    /* close and fsync the files */ 
    if(fsync(fdo) ==-1){ 
     PERROR; 
     exit(errno); 
    } 
    if(close(fdo) == -1){ 
     PERROR; 
     exit(errno); 
    } 
    if(close(fdi) == -1){ 
     PERROR; 
     exit(errno); 
    } 

    /* if it survived to here, all worked. */ 
    exit(0); 
} 
+0

IcedTea OpenJDK 1.6 जावा, ओपनएसयूएसई 11 लिनक्स, 4 कोर-सीपीयू, 4 जीबी, 1 एसएटीए-एचडी एक जेबीओडी से फाइबर चैनल पर। – dmeister

+0

मैंने एक ही यादृच्छिक डेटा के 64 के ब्लॉक का उपयोग करके एक 4 जीबी फ़ाइल लिखी और फाइल को खुली और फ़ाइल बंद करने के बीच का समय मापा (और सिंक होने पर सिंक)। – dmeister

+0

कोई अन्य वर्कलोड? सी जीसीसी> 4 के साथ था? यह कॉन्फ़िगरेशन एक जैसा है जिसे मैंने एसटीके (आरआईपी) और 120 एमबी/एस में कोशिश की है, वह काफी सराहनीय है। –

0

सी कोड उप-शीर्ष हो सकता है, क्योंकि यह कच्चे ओएस लिखने() के बजाय stdio का उपयोग करता है। लेकिन फिर, जावा अधिक इष्टतम हो सकता है क्योंकि यह बड़े बफर आवंटित करता है?

वैसे भी, आप केवल एपीआईडीओसी पर भरोसा कर सकते हैं। बाकी आपके कर्तव्यों से परे है।

6

वास्तव में, सी में आप केवल एक फ़ाइल वर्णनकर्ता पर fsync(), नहीं sync() (या "सिंक" कमांड) कॉल करने के लिए जो flush करने के लिए डिस्क सिस्टम-वाइड करने के लिए सभी बफ़र्स गिरी का संकेत चाहते हैं।

यदि आप strace (यहां लिनक्स-विशिष्ट प्राप्त कर रहे हैं) JVM आपको अपनी आउटपुट फ़ाइल पर fsync() या fdatasync() सिस्टम कॉल करने में सक्षम होना चाहिए। यही वह होगा जो मैं getFD() की अपेक्षा करता हूं। sync() करने के लिए कॉल करें। मुझे लगता है कि c.force(true) बस एनआईओ को झंडे लगाता है कि fsync() प्रत्येक लेखन के बाद बुलाया जाना चाहिए। यह हो सकता है कि आप जिस JVM का उपयोग कर रहे हैं वह वास्तव में sync() कॉल लागू नहीं करता है?

मुझे यकीन नहीं है कि आप "सिंक" को कमांड के रूप में कॉल करते समय कोई अंतर क्यों नहीं देख रहे थे: लेकिन जाहिर है, पहले सिंक आमंत्रण के बाद, बाद वाले लोग आमतौर पर बहुत तेज होते हैं।दोबारा, मैं strace (सोलारिस पर ट्रेस) को तोड़ने के इच्छुक हूं "वास्तव में क्या हो रहा है?" उपकरण।

+0

सिस्कोल का पता लगाने का विचार अच्छा है। मैं इसे कल करूंगा। – dmeister

+0

बल() fsync या fdatasync (मेटाडेटा ध्वज के आधार पर) को कॉल करता है। हालांकि, यह प्रत्येक कॉल के बाद सीधे fsync/fdatasync को कॉल करने के लिए एक राज्य सेट नहीं करता है। मैंने इसे ओपनजेडीके स्रोत कोड में देखा। – dmeister

0

(मैं जानता हूँ कि यह एक बहुत ही देर जबाब है, लेकिन मैं इस सूत्र एक गूगल खोज कर रही है में भाग, और कहा कि शायद तुम कैसे यहाँ भी समाप्त हो गया है।)

आपका फोन करने के लिए एक एकल पर सिंक() जावा में फ़ाइल डिस्क्रिप्टर, इसलिए केवल उस फ़ाइल से संबंधित बफर डिस्क पर फंस गए हैं।

सी और कमांड लाइन में, आप संपूर्ण ऑपरेटिंग सिस्टम पर सिंक() को कॉल कर रहे हैं - इसलिए प्रत्येक फ़ाइल बफर डिस्क पर फिसल जाता है, आपके ओ/एस सब कुछ कर रहा है।

तुलनीय होने के लिए, सी कॉल syncfs (fp) होना चाहिए;

लिनक्स आदमी पृष्ठ से:

sync() causes all buffered modifications to file metadata and data to 
    be written to the underlying file systems. 

    syncfs() is like sync(), but synchronizes just the file system contain‐ 
    ing file referred to by the open file descriptor fd. 
+1

syncfs() सिंक() बेहतर नहीं है, दोनों गलत है। Fdatasync() कॉल वह है जो जावा उपयोग करता है और जिसे आप सी में उपयोग करना चाहते हैं। – eckes

2

यह एक अच्छा विचार सिंक्रनाइज़ आई/ओ डेटा अखंडता पूरा होने का प्रयोग है। हालांकि आपका सी नमूना गलत विधि का उपयोग कर रहा है। आप sync() का उपयोग करते हैं, जिसका उपयोग पूरे ओएस को सिंक करने के लिए किया जाता है।

आप डिस्क के लिए है कि एक फ़ाइल के ब्लॉक लिखना चाहते हैं, तो आप BTW सी में fsync(2) या fdatasync(2) उपयोग करने की आवश्यकता है: जब आप सी में बफ़र stdio का उपयोग (या एक BufferedOutputStream या जावा में कुछ लेखक) आप की जरूरत है सिंक करने से पहले पहले दोनों फ्लश करें।

fdatasync() संस्करण कुछ और अधिक कुशल है यदि फ़ाइल सिंक होने के बाद नाम या आकार नहीं बदला है। लेकिन यह सभी मेटा डेटा को भी रोक नहीं सकता है। यदि आप अपना खुद का लेनदेन संबंधी सुरक्षित डेटाबेस सिस्टम लिखना चाहते हैं, तो आपको कुछ और सामानों को देखना होगा (जैसे मूल निर्देशिका को फ़िसस करना)।

संबंधित मुद्दे