2016-01-06 12 views
5

Dizeleri bir dosyaya yazdıran bir Go programım var. 20000 kez yinelenen bir döngü var ve her yinelemede bir dosyaya 20-30 dizeleri yazıyorum. Sadece bir dosyaya yazmanın en iyi yolunun hangisi olduğunu bilmek istedim.Yazma İşlemi Maliyeti [Go lang]

  • Yaklaşım 1: kodunun başlangıcında dosya işaretçisi açık tutun ve her dize için yazmak. 20000 * 30 yazma işlemi yapar.

  • Yaklaşım 2: bytes.Buffer Git ve tampon her şeyi depolamak ve dosya işaretçisi kodunun başına veya sonuna açılmalıdır bu durumda end.Also de yazmak kodu. önemli mi?

Yaklaşım 2'nin daha iyi çalışması gerektiğini varsayıyorum. Birisi bunu bir sebeple teyit edebilir. Yazma, bir kerede periyodik olarak yazmaktan nasıl daha iyidir. Çünkü dosya işaretçisi zaten açık olacak. f.WriteString(<string>) ve buffer.WriteString(<some string>) arabellek kullanıyorum bytes.Buffer türünde ve f dosya tanıtıcısı açık. Dosyalarda yazarken zaman alan işlemler syscalls ve disk G/Ç'tır.

cevap

3

Dosya işaretçisinin açık olması gerçeği size bir bedel ödemiyor. Yani, saf olarak, ikinci yöntemin en iyi olduğunu söyleyebiliriz.

Şimdi, bildiğiniz gibi, işletim sistemi doğrudan dosyalara yazmaz, yazılan dosyalar için dahili bir bellek içi önbellek kullanır ve daha sonra gerçek G/Ç'yi yapar. Bunun ayrıntılarını bilmiyorum ve genel olarak ihtiyacım yok.

Tavsiye etmem gerekenler orta zemin çözümdür: her döngü yineleme için bir tampon yapın ve bunu bir N kere yazın. Bu sayede syscall sayısı ve (potansiyel olarak) disk yazmalarının büyük bir kısmını kestirmek, fakat tampon ile çok fazla bellek tüketmeden (dizelerinizin büyüklüğüne bağlı olarak, benim göz önünde bulundurulması gereken bir nokta).

En iyi çözüm için kıyaslama yapmayı öneririm, ancak sistem tarafından yapılan önbellekleme nedeniyle, disk I/O karşılaştırması gerçek bir kabus.

+0

Bu, ['bufio'] 'nun (https://golang.org/pkg/bufio/) adresi için geçerlidir. – JimB

1

Sistem çağrıları ucuz değildir, bu nedenle ikinci yaklaşım daha iyidir. Sadece write aramaya

$ ./lat_syscall write 
Simple write: 0.1522 microseconds 

Yani, benim sistemde yaklaşık 20000 * 0.15μs = 3ms ekstra zaman alacaktır:

Bunu write tek aramak ne kadar sürdüğünü ölçmek için lmbench gelen lat_syscall aracını kullanabilirsiniz her dize için. Bu tür bir görev için tam olarak oluşturulmuş olan

5

bufio paketi tam olarak oluşturulmuştur. Her bir Yazma çağrısı için bufio.Writer bir sistem çağrısı yapmak yerine, bir syscall yapmadan önce dahili bellekteki sabit bir bayt sayısına kadar arabelleğe alınabilir.Bir sistem çağrısı sonra iç tampon bufio.Writer

  • daha syscalls (N/S yerine 1 arasında)
  • daha az bellek kullanmaktadır (S bayt yerine yapan ikinci bir yaklaşım ile karşılaştırıldığında veriler

    sonraki kısmı için yeniden kullanılır N bayt) S

- tampon boyutu (ile belirtilebilir olan 0), N - yazılması gereken toplam veri boyutu.

Örnek kullanım (https://play.golang.org/p/AvBE1d6wpT): OS zaten diske bir senkronize önce tampon olacağını zaman

f, err := os.Create("file.txt") 
if err != nil { 
    log.Fatal(err) 
} 
defer f.Close() 

w := bufio.NewWriter(f) 
fmt.Fprint(w, "Hello, ") 
fmt.Fprint(w, "world!") 
err = w.Flush() // Don't forget to flush! 
if err != nil { 
    log.Fatal(err) 
} 
+0

İkinci yaklaşım, sabit miktarda bellek gerektiriyor. Bunu açıklayabilir misiniz? Çünkü tampon gereksinime göre büyümeye devam ediyor. – KD157

+0

evet, düzenleme için teşekkürler, ama yine de tüm baytları bir kerede daha hızlı yazabiliyor musunuz? Her birinde 1000 karakterden oluşan 20.000 dizi olduğunu varsayalım. Bu neredeyse 20 MB. – KD157

+0

Bunu ölçebilirsiniz. Ancak, 4kb arabelleği 20Mb yazmak için gerekli 5000 syscalls yükü bir milisaniyeden daha azdır. Ayrıca, bayt oluşturmadan önce yazmanız gereken son veri boyutunu bilmiyorsanız, Buffer, (bytes.Buffer) büyük olasılıkla yeniden boyutlandırılmalı ve kesinlikle uygulamanızı yavaşlatır. – kostya

0

Ben io.Writer bir bufio.Writer kullanarak yerine sadece bir havai ve kod karmaşıklığını soru. Arabayı tamponlamak gibi. Bunun, kodunuzda sonsuza dek tanıtılmaya değer olduğunu görmek için sisteminizde kıyaslama yapmanız gerektiğini düşünüyorum.