[EDIT]Windows API, BinaryWriter'den çok daha hızlı görünüyor - testim doğru mu?
@VilleKrumlinde Teşekkürler Bir Kod Analizi uyarısından kaçınmaya çalışırken yanlışlıkla daha önce tanıttığım bir hatayı düzelttim. Yanlışlıkla dosya uzunluğunu sıfırlayan "örtüşen" dosya işlemeyi açıyordum. Bu şimdi düzeltildi ve sorun olmadan aynı akış için birden çok kez FastWrite()
'u arayabilirsiniz.
[Bitiş Düzenle]
Bakış
Ben diske yapılar yazma Dizilerin iki farklı şekilde karşılaştırmak için bazı zamanlama testleri yapıyorum. Algılanan bilgeliğin, G/Ç maliyetlerinin diğer şeylere kıyasla çok yüksek olduğuna ve diğer şeyleri optimize etmeye çok fazla zaman harcamaya değmeyeceğine inanıyorum. Ancak, zamanlama testlerim aksini gösteriyor gibi görünüyor. Ya bir hata yapıyorum (ki bu tamamen mümkündür) ya da optimizasyonum gerçekten çok önemli.
İlk bazı tarih Tarih: Bu FastWrite()
yöntem başlangıçta yazılmıştır yıl önce eski C++ program tarafından tüketildiği bir dosyaya yazma yapılar desteklemek ve biz hala bu amaçla bunu kullanıyor. (Ayrıca bir FastRead()
yöntemine karşılık gelir.) Öncelikle bir dosyaya blittable struct dizileri yazmayı kolaylaştırmak için yazılmıştı ve hızı ikincil bir endişeydi.
Bu gibi optimizasyonların sadece BinaryWriter
kullanmasından çok daha hızlı olmadığı bir kişiden fazla kişi tarafından söylendi, bu yüzden sonunda mermiyi ısırdım ve bazı zamanlama testleri yaptım. 50 kat daha hızlı BinaryWriter
kullanarak eşdeğer daha - sonuçlar
Bu benim FastWrite()
yöntem 30 olduğunu görünür ... beni şaşırttı var. Bu çok saçma görünüyor, bu yüzden ben kimsenin hataları bulabilmesini görmek için kodumu buraya gönderiyorum.
Sistem Özellikleri ayıklayıcıya DIŞINDA çalıştırılan bir x86 AÇIKLAMASI yapı, test
- .
- Windows 8, x64, 16 GB bellekte çalışıyor.
- Normal bir sabit sürücüde (SSD'de değil) çalıştırın.
Sonuçları
Benim sonuçları (şimdiye .Net 4.5 yüklü) Visual Studio 2012 ile .Net 4 Kullanılması
SlowWrite() took 00:00:02.0747141
FastWrite() took 00:00:00.0318139
SlowWrite() took 00:00:01.9205158
FastWrite() took 00:00:00.0327242
SlowWrite() took 00:00:01.9289878
FastWrite() took 00:00:00.0321100
SlowWrite() took 00:00:01.9374454
FastWrite() took 00:00:00.0316074
Gördüğünüz gibi, görünüyor Bu çalışmada FastWrite()
'un 50 kat daha hızlı olduğunu göstermek için.
İşte test kodum. Testi yürüttükten sonra, gerçekten aynı olduklarını doğrulamak için iki dosyanın ikili bir karşılaştırmasını yaptım (örn.FastWrite()
ve SlowWrite()
aynı dosyaları üretti).
Ne yapabileceğinizi görün. :) şöyle
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;
namespace ConsoleApplication1
{
internal class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TestStruct
{
public byte ByteValue;
public short ShortValue;
public int IntValue;
public long LongValue;
public float FloatValue;
public double DoubleValue;
}
static void Main()
{
Directory.CreateDirectory("C:\\TEST");
string filename1 = "C:\\TEST\\TEST1.BIN";
string filename2 = "C:\\TEST\\TEST2.BIN";
int count = 1000;
var array = new TestStruct[10000];
for (int i = 0; i < array.Length; ++i)
array[i].IntValue = i;
var sw = new Stopwatch();
for (int trial = 0; trial < 4; ++trial)
{
sw.Restart();
using (var output = new FileStream(filename1, FileMode.Create))
using (var writer = new BinaryWriter(output, Encoding.Default, true))
{
for (int i = 0; i < count; ++i)
{
output.Position = 0;
SlowWrite(writer, array, 0, array.Length);
}
}
Console.WriteLine("SlowWrite() took " + sw.Elapsed);
sw.Restart();
using (var output = new FileStream(filename2, FileMode.Create))
{
for (int i = 0; i < count; ++i)
{
output.Position = 0;
FastWrite(output, array, 0, array.Length);
}
}
Console.WriteLine("FastWrite() took " + sw.Elapsed);
}
}
static void SlowWrite(BinaryWriter writer, TestStruct[] array, int offset, int count)
{
for (int i = offset; i < offset + count; ++i)
{
var item = array[i]; // I also tried just writing from array[i] directly with similar results.
writer.Write(item.ByteValue);
writer.Write(item.ShortValue);
writer.Write(item.IntValue);
writer.Write(item.LongValue);
writer.Write(item.FloatValue);
writer.Write(item.DoubleValue);
}
}
static void FastWrite<T>(FileStream fs, T[] array, int offset, int count) where T: struct
{
int sizeOfT = Marshal.SizeOf(typeof(T));
GCHandle gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
try
{
uint bytesWritten;
uint bytesToWrite = (uint)(count * sizeOfT);
if
(
!WriteFile
(
fs.SafeFileHandle,
new IntPtr(gcHandle.AddrOfPinnedObject().ToInt64() + (offset*sizeOfT)),
bytesToWrite,
out bytesWritten,
IntPtr.Zero
)
)
{
throw new IOException("Unable to write file.", new Win32Exception(Marshal.GetLastWin32Error()));
}
Debug.Assert(bytesWritten == bytesToWrite);
}
finally
{
gcHandle.Free();
}
}
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WriteFile
(
SafeFileHandle hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped
);
}
}
Takibi
Ben de @ ErenErsönmez önerdiği kodunu test ettik (ve her üç dosyanın testin sonunda özdeş olduğu doğrulandıktan):
static void ErenWrite<T>(FileStream fs, T[] array, int offset, int count) where T : struct
{
// Note: This doesn't use 'offset' or 'count', but it could easily be changed to do so,
// and it doesn't change the results of this particular test program.
int size = Marshal.SizeOf(typeof(TestStruct)) * array.Length;
var bytes = new byte[size];
GCHandle gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
try
{
var ptr = new IntPtr(gcHandle.AddrOfPinnedObject().ToInt64());
Marshal.Copy(ptr, bytes, 0, size);
fs.Write(bytes, 0, size);
}
finally
{
gcHandle.Free();
}
}
Bu kod için bir sınama ekledim ve aynı zamanda output.Position = 0;
satırlarını kaldırdım, böylece dosyalar artık 263K'ye çıkıyor (bu da makul boyutta). Bu değişikliklerle
sonuçlar şöyle:
NOT Bak en FastWrite()
zamanlardır yavaş ne kadar zaman yapamaz tutmak geri dosya işaretçisi sıfırlama sıfır !:
SlowWrite() took 00:00:01.9929327
FastWrite() took 00:00:00.1152534
ErenWrite() took 00:00:00.2185131
SlowWrite() took 00:00:01.8877979
FastWrite() took 00:00:00.2087977
ErenWrite() took 00:00:00.2191266
SlowWrite() took 00:00:01.9279477
FastWrite() took 00:00:00.2096208
ErenWrite() took 00:00:00.2102270
SlowWrite() took 00:00:01.7823760
FastWrite() took 00:00:00.1137891
ErenWrite() took 00:00:00.3028128
Bu nedenle, Windows API'sini kullanmak zorunda kalmadan, olmadan, Marshaling 'u kullanarak neredeyse aynı hıza ulaşabilirsiniz. Tek dezavantajı, Eren'in yönteminin tüm yapı dizisinin bir kopyasını yapmak zorunda olmasıdır, bu da bellek sınırlıysa bir sorun olabilir.
_Well_, başlık daha iyi olurdu? http://meta.stackexchange.com/questions/10647/how-do-i-write-a-good-title –
İlk döngü "FastWrite" ve ikinci döngü "SlowWrite" olarak adlandırılırsa sonuçlar nedir? –
@DanielHilgarth Fark etmez, ama bunu beklemezdim (çünkü bu etkiyi en aza indirgemek için dış çevrime sahibim). –