PDA

View Full Version : آموزش: بافر کردن فایل



josh simpson
یک شنبه 15 بهمن 1391, 17:59 عصر
من از روش های مختلفی برای بافر کردن فایل ها تا کنون استفاده کردم و به نظرم این روشی را که برای شما قرار دادم بهترین روش تاکنون بوده
به هر حال اگر دوستان روش های بهتری رو برای این کار دارن خوشحال میشم یاد بگیرم.:چشمک:



private void ReadFile()
{
FileStream streamReader1 = new FileStream("File Path", FileMode.Open, FileAccess.Read, FileShare.Read);
streamReader1.Position = 0;
while (true)
{
// میزان حجم باقی مانده
long remainFilelen = streamReader1.Length - streamReader1.Position;
// اگر تمام فایل خوانده شده باشد
if (remainFilelen == 0)
{
break;
}
//
byte[] buffer1 = new byte[0];
Microsoft.VisualBasic.Devices.ComputerInfo computerInfo = new Microsoft.VisualBasic.Devices.ComputerInfo();
// اگر حجم باقی مانده بیشتر از 10 مگ باشد و سیستم بیشتر از 200 مگ رم خالی داشته باشد
if (remainFilelen >= 10485760 && computerInfo.AvailablePhysicalMemory >= 209715200)
{
// بافر 10 مگی
buffer1 = new byte[10485760];
}
// اگر حجم باقی مانده بیشتر از 1 مگ باشد و سیستم بیشتر از 100 مگ رم خالی داشته باشد
else if (remainFilelen >= 1048576 && computerInfo.AvailablePhysicalMemory >= 104857600)
{
// بافر 1 مگی
buffer1 = new byte[1048576];
}
// اگر حجم باقی مانده بیشتر از 100 کیلو باشد و سیستم بیشتر از 10 مگ رم خالی داشته باشد
else if (remainFilelen >= 102400 && computerInfo.AvailablePhysicalMemory >= 10485760)
{
// بافر 100 کیلویی
buffer1 = new byte[102400];
}
// اگر حجم باقی مانده بیشتر از 10 کیلو باشد
else if (remainFilelen >= 10240)
{
// بافر 10 کیلویی
buffer1 = new byte[10240];
}
// اگر حجم باقی مانده کمتر از 10 کیلو باشد
else if (remainFilelen < 10240)
{
// بافر به اندازه حجم فایل
buffer1 = new byte[remainFilelen];
}
//reed data
streamReader1.Read(buffer1, 0, buffer1.Length);
//
// از بین بردن بافر و خالی کردن حافظه
buffer1 = null;
GC.Collect();
//
}
}

the king
یک شنبه 15 بهمن 1391, 18:28 عصر
من از روش های مختلفی برای بافر کردن فایل ها تا کنون استفاده کردم و به نظرم این روشی را که برای شما قرار دادم بهترین روش تاکنون بوده
به هر حال اگر دوستان روش های بهتری رو برای این کار دارن خوشحال میشم یاد بگیرم.:چشمک:



private void ReadFile()
{
FileStream streamReader1 = new FileStream("File Path", FileMode.Open, FileAccess.Read, FileShare.Read);
streamReader1.Position = 0;
while (true)
{
// میزان حجم باقی مانده
long remainFilelen = streamReader1.Length - streamReader1.Position;
// اگر تمام فایل خوانده شده باشد
if (remainFilelen == 0)
{
break;
}
//
byte[] buffer1 = new byte[0];
Microsoft.VisualBasic.Devices.ComputerInfo computerInfo = new Microsoft.VisualBasic.Devices.ComputerInfo();
// اگر حجم باقی مانده بیشتر از 10 مگ باشد و سیستم بیشتر از 200 مگ رم خالی داشته باشد
if (remainFilelen >= 10485760 && computerInfo.AvailablePhysicalMemory >= 209715200)
{
// بافر 10 مگی
buffer1 = new byte[10485760];
}
// اگر حجم باقی مانده بیشتر از 1 مگ باشد و سیستم بیشتر از 100 مگ رم خالی داشته باشد
else if (remainFilelen >= 1048576 && computerInfo.AvailablePhysicalMemory >= 104857600)
{
// بافر 1 مگی
buffer1 = new byte[1048576];
}
// اگر حجم باقی مانده بیشتر از 100 کیلو باشد و سیستم بیشتر از 10 مگ رم خالی داشته باشد
else if (remainFilelen >= 102400 && computerInfo.AvailablePhysicalMemory >= 10485760)
{
// بافر 100 کیلویی
buffer1 = new byte[102400];
}
// اگر حجم باقی مانده بیشتر از 10 کیلو باشد
else if (remainFilelen >= 10240)
{
// بافر 10 کیلویی
buffer1 = new byte[10240];
}
// اگر حجم باقی مانده کمتر از 10 کیلو باشد
else if (remainFilelen < 10240)
{
// بافر به اندازه حجم فایل
buffer1 = new byte[remainFilelen];
}
//reed data
streamReader1.Read(buffer1, 0, buffer1.Length);
//
// از بین بردن بافر و خالی کردن حافظه
buffer1 = null;
GC.Collect();
//
}
}


هدف تون بهینه سازی یه اما در عمل اینکار رو انجام نمی دهید.
شما داخل اون حلقه while هر بار و سر خواندن هر تکه ای مجددا یک بافر جدید می سازید، قبلی رو مرخص می کنید
و یک نمونه جدید می سازید. اولا اون حافظه ای که مرخص می کنید الزاما همون موقع آزاد نمیشه، ثانیا یک بار اضافی یه.
نیازی نیست اون بافر قبلی رو رها کنید، همون یک نمونه اولی که تخصیص می دهید برای کل فایل کافیه. این بافر ای که شما در نظر می گیرید اون بافر ای که ویندوز
برای خواندن از سیستم فایل در نظر می گیره یا بافری که هارد دیسک برای خواندن سکتور هاش در نظر می گیره نیست. حجم اش اگه خیلی کم باشه تعداد
درخواست های زیاد سر بار داره، اما در حجم بالا تاثیر محسوسی نداره. نیازی نیست که اینقدر روی حجم اش مانور بدهید، مخصوصا که موقع Read کردن خودتان
حجمی که ازش استفاده می شه رو مشخص می کنید، می توانید همان بافر یک مگابایتی رو داشته باشید اما فقط سه بایت داخلش بخوانید.


private void ReadFile(string path)
{
using (FileStream streamReader1 = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
long remainFilelen = streamReader1.Length;
byte[] buffer1 = new byte[1048576];
while (remainFilelen > 0)
{
int length = (int)Math.Min(remainFilelen, buffer1.Length);
streamReader1.Read(buffer1, 0, length);
remainFilelen -= length;
}
}
}

FastCode
یک شنبه 15 بهمن 1391, 18:30 عصر
روشتون نمره ۲ از ۱۰ میگیره
اون ۲ نمره هم برای اینه که توی ویندوز های قدیمی ممکنه بهتر از هیچی باشه


روش بهتر استفاده از یک کلاس این شکلیه:
البته این خودش نمره ۶ از ۱۰ میگیره چون خیلی نقص داره.
بیشتر از این از سطح سایت خارجه.



public class ParallelPagedEnumerator<T>
{
bool mStarted; public bool Started { get { return mStarted; } }
bool mDone; public bool Done { get { return mDone; } }

Func<object, T> mGetNextItem;
System.Threading.Thread Thread_Fetch;
System.Threading.AutoResetEvent ARE_FETCH = new System.Threading.AutoResetEvent (false);
System.Threading.AutoResetEvent ARE_GET = new System.Threading.AutoResetEvent (false);
System.Collections.Concurrent.ConcurrentBag<T[]> mPages;
ushort mPageSize; public ushort PageSize { get { return mPageSize; } set { mPageSize = value; } }
byte mPagesAhead; public byte PagesAhead { get { return mPagesAhead; } set { mPagesAhead = value; } }
object state;
//, int FetchThreads = 1
public ParallelPagedEnumerator(Object State, Func<object, T> GetNextItem, ushort PageSize = 4096, byte PagesAhead = 4)
{
state = State;
mStarted = false;
mDone = false;
mGetNextItem = GetNextItem;
mPageSize = PageSize;
mPagesAhead = PagesAhead;
mPages = new System.Collections.Concurrent.ConcurrentBag<T[]> ();
Thread_Fetch = new System.Threading.Thread (FetchAll);
}
void FetchAll ()
{
ushort Offset = 0;
T[] Items = null;
do
{
if(Items == null)
Items = new T[PageSize];
T Item = mGetNextItem(state);
if(Item == null)
{
mPages.Add(Items);
AutoResetEvent ARE = ARE_GET;
ARE_GET = null;
ARE.Set();
mDone = true;
Items = null;
return;
}
Items [Offset++] = Item;
if(Offset >= mPageSize)
{
mPages.Add(Items);
Offset = 0;
Items = null;
ARE_GET.Set();
if(mPages.Count > mPagesAhead)
{
ARE_FETCH.WaitOne();
}
}
} while(true);
}
public void Start ()
{
if(mStarted)
throw new InvalidOperationException("The Enumerator has already started.");
Thread_Fetch.Start();
mStarted = true;
}
public bool GetNextPage (int TimeOut, out T[] Page)
{
if(!mStarted)
throw new InvalidOperationException("The Enumerator MUST Start() before calling GetNextPage.");
if (mPages.Count > 0) {
if (mPages.Count <= mPagesAhead)
ARE_FETCH.Set ();
if (mPages.TryTake (out Page))
return true;
}
if (ARE_GET == null) {
Page = null;
return false;
}
do
{
ARE_FETCH.Set ();
ARE_GET.WaitOne (TimeOut);
if (mPages.Count > 0) {
if(mPages.TryTake(out Page))
return true;
}
}while(!mDone);
Page = null;
return false;
}
}
}

این کد تست نشده
License:MIT

زمانی که منتظر هستید تا تکه خونده شده پردازش بشه خیلی مهمتره
این کلاس برای خوندن فایل طراحی شده ولی باید یک T مناسب بهش بدید که به ازای هر Chunk خوانده شده offset رو هم نگهداری کنه

josh simpson
یک شنبه 15 بهمن 1391, 19:14 عصر
هدف تون بهینه سازی یه اما در عمل اینکار رو انجام نمی دهید.
شما داخل اون حلقه while هر بار و سر خواندن هر تکه ای مجددا یک بافر جدید می سازید، قبلی رو مرخص می کنید
و یک نمونه جدید می سازید. اولا اون حافظه ای که مرخص می کنید الزاما همون موقع آزاد نمیشه، ثانیا یک بار اضافی یه.
نیازی نیست اون بافر قبلی رو رها کنید، همون یک نمونه اولی که تخصیص می دهید برای کل فایل کافیه. این بافر ای که شما در نظر می گیرید اون بافر ای که ویندوز
برای خواندن از سیستم فایل در نظر می گیره یا بافری که هارد دیسک برای خواندن سکتور هاش در نظر می گیره نیست. حجم اش اگه خیلی کم باشه تعداد
درخواست های زیاد سر بار داره، اما در حجم بالا تاثیر محسوسی نداره. نیازی نیست که اینقدر روی حجم اش مانور بدهید، مخصوصا که موقع Read کردن خودتان
حجمی که ازش استفاده می شه رو مشخص می کنید، می توانید همان بافر یک مگابایتی رو داشته باشید اما فقط سه بایت داخلش بخوانید.


در مورد اون قسمتی که بافر آزاد می شه


buffer1 = null;
GC.Collect();


اگر این کار انجام نشه زمانی که حجم فایل مثلا 1 گیگ باشه و بافر هم همون 1 مگ که شما گفتین باشه
خواهید دید که حجم استفاده از RAM به مقدار زیادی تا حتی چند صد مگ هم افزایش پیدا میکنه ولی با
این کار از استفاده زیاد از RAM جلوگیری میشه.
در تست هایی که من انجام دادم بافر 10 مگی برای فایل های حجیم زمان خیلی کمتری داشت .
از طرفی برای بافر 10 مگی یا 1 مگی سیستم باید رم خالی کافی داشته باشه وگرنه باعث استفاده از پیج فایل و کاهش سرعت و ... خواهد شد.

FastCode
یک شنبه 15 بهمن 1391, 19:43 عصر
در مورد اون قسمتی که بافر آزاد می شه


buffer1 = null;
GC.Collect();


اگر این کار انجام نشه زمانی که حجم فایل مثلا 1 گیگ باشه و بافر هم همون 1 مگ که شما گفتین باشه
خواهید دید که حجم استفاده از RAM به مقدار زیادی تا حتی چند صد مگ هم افزایش پیدا میکنه ولی با
این کار از استفاده زیاد از RAM جلوگیری میشه.
در تست هایی که من انجام دادم بافر 10 مگی برای فایل های حجیم زمان خیلی کمتری داشت .



من شخصا ترجیح میدم برنامه های روی سیستمم همه حجم رو استفاده کنن
اگر بخوام استفاده نکنن روشون limit میزارم
http://docs.oracle.com/cd/E19082-01/819-2239/ulimit-1/index.html

josh simpson
یک شنبه 15 بهمن 1391, 20:25 عصر
من شخصا ترجیح میدم برنامه های روی سیستمم همه حجم رو استفاده کنن
اگر بخوام استفاده نکنن روشون limit میزارم


روش Limit شما مشکلات زیادی داره چون زمانی که شما از این روش استفاده می کنید در واقع Working Set
برنامتون به قسمت modified memory منتقل میشه و از طرفی سیستم این قسمت رو به پیج فایل منتقل میکنه
و اگر شما مثلا 1 مگ یا 10 مگ یا هر چقدر بافر داشته باشید و به پیج فایل منتقل شده باشه، برنامتون باید زمان
زیادی رو منتظر بمونه تا اونها از پیج فایل دوباره به RAM منتقل بشن.

در حالی که در روش من اطلاعات اضافی کاملا از بین میرن و به modified memory منتقل نمیشن.

FastCode
یک شنبه 15 بهمن 1391, 21:41 عصر
روش Limit شما مشکلات زیادی داره چون زمانی که شما از این روش استفاده می کنید در واقع Working Set
برنامتون به قسمت modified memory منتقل میشه و از طرفی سیستم این قسمت رو به پیج فایل منتقل میکنه
و اگر شما مثلا 1 مگ یا 10 مگ یا هر چقدر بافر داشته باشید و به پیج فایل منتقل شده باشه، برنامتون باید زمان
زیادی رو منتظر بمونه تا اونها از پیج فایل دوباره به RAM منتقل بشن.

در حالی که در روش من اطلاعات اضافی کاملا از بین میرن و به modified memory منتقل نمیشن.
اتفاقاً برعکس
این چیزی که شما گفتید دقیقاً عکس چیزیه که من لینکش رو گزاشتم
در این حالت فقط Virtual Memory محدود میشه
VM_LOCKED
VM_RESERVED
و
VM_COMMIT
رو جست و جو کنید

josh simpson
یک شنبه 15 بهمن 1391, 21:59 عصر
اتفاقاً برعکس
این چیزی که شما گفتید دقیقاً عکس چیزیه که من لینکش رو گزاشتم
در این حالت فقط Virtual Memory محدود میشه
VM_LOCKED
VM_RESERVED
و
VM_COMMIT
رو جست و جو کنید
میشه لطفا آموزشی که لینکش رو گذاشتین به طور واضح تر توضیح بدین و نحوه استفادش در C#‎ رو بگین؟

FastCode
دوشنبه 16 بهمن 1391, 00:24 صبح
توی C# فقط توی محیط ۳۲ بیت به شکل مصنوعی میشه این کار رو انجام داد
با API ه VirtualAlloc
در لینوکس با همون ulimit