Добрый день, вопрос скорее теоретический, но очень интересно от чего так получилось. И если есть предложения, то как бы убыстрить новообразованный код.
Код буду предоставлять без подробностей реализации, дело не в них и так читать удобнее.
Я создаю файлы основываясь на других файлах. Способ который был до снижения скорости, но были проблемы с памятью представлен далее. Если в двух словах, то я считывал один файл, создавал на его основе сразу же 20 - 50 списков (для каждого нового файла) и хранил их в ассоциативном массиве, после наступления определенного условия, часть из списков сохранялась в файл и удалялась из массива. Но они сильно накапливались и пришлось отказаться от этого из за ошибки недостаточности памяти Основные интересующие меня в данном вопросе процедуры представлены ниже:
delegate double ToDouble(string num);
delegate void DataManager(MqlTick tick,
string key,
ref SortedList<string, List<MqlTick>> tickData,
ref SortedList<string, List<MqlRates>> ratesData,
ref bool addRow);
delegate double SmileToPrice(smileData IV_data, double strike, bool isCall);
delegate void CheckDirectories(string pathToFolder, string dirName);
delegate string WriteConverter<T>(T Data);
class Converter
{
public void createQuotes(inputData data)
{
// запускаем создание файлов
}
private DateTime getExpiryMonth(string futName)
{
// Получаем дату из переданой строки
}
private List<QuotesDetales> fileReader(ref SortedList<string, List<MqlTick>> tickData,
ref SortedList<string, List<MqlRates>> ratesData,
inputData data,
DateTime DT_border,
string selectedFile,
DateTime fileMonth,
ref Dictionary<string, List<double>> strikeCollection)
{
List<QuotesDetales> expiredAssets // устанавливаю
// Select asset by dates
DateTime start // устанавливаю
DateTime end = // устанавливаю
// String to double
ToDouble strToDouble = (string num) =>
{
// Преобразовываем число из строки в double (часть файлов с запятыми, часть с точками ввиде разделителя идут)
};
// Add data
DataManager addData = (MqlTick tick,
string key,
ref SortedList<string, List<MqlTick>> ticks,
ref SortedList<string, List<MqlRates>> rates,
ref bool addRow) =>
{
// Заполняем tickData и ratesData (переданые по ссылкам как ticks и rates)
};
// Calculation price
SmileToPrice getPrice = (smileData IV_data, double strike, bool isCall) =>
{
// Считаем значение цены
};
// reading file
using (StreamReader reader = File.OpenText(selectedFile))
{
reader.ReadLine();
string line;
while ((line = reader.ReadLine()) != null)
{
if (!string.IsNullOrEmpty(line) && !string.IsNullOrWhiteSpace(line))
{
string[] thisLine = line.Split(';');
DateTime expiryMonth = getExpiryMonth(thisLine[0]);
if (thisLine[0].Substring(0, data.mask.Length).CompareTo(data.mask) == 0 &&
expiryMonth.CompareTo(start) >= 0 && expiryMonth.CompareTo(end) <= 0)
{
DateTime currentDT = DateTime.ParseExact(thisLine[2], "yyyyMMddHHmmssfff", null);
if (strToDouble(thisLine[4]) != 0 &&
strToDouble(thisLine[10]) > 0 &&
currentDT.CompareTo(data.from) >= 0)
{
smileData IV_data = new smileData
{
Price = strToDouble(thisLine[3]),
A = strToDouble(thisLine[5]),
B = strToDouble(thisLine[6]),
C = strToDouble(thisLine[7]),
D = strToDouble(thisLine[8]),
E = strToDouble(thisLine[9]),
S = strToDouble(thisLine[4]),
T = strToDouble(thisLine[10])
};
List<double> strikes = new List<double>();
if (strikeCollection.ContainsKey(thisLine[0]))
strikes = strikeCollection[thisLine[0]];
else
strikeCollection.Add(thisLine[0], strikes);
getStrike(IV_data.Price, data.stepStrikes, data.numStrikes, ref strikes);
strikeCollection[thisLine[0]] = strikes;
MqlTick tick = new MqlTick
{
time = currentDT,
volume = 0
};
bool addRow = false;
foreach (double K in strikes)
{
for (int i = 0; i < 2; i++)
{
tick.last = getPrice(IV_data, K, (i == 0 ? true : false));
tick.bid = tick.last;
tick.ask = tick.last;
if (data.spreadBidAsk > 0)
{
// Делаем еще некоторые действия с преобразованием цены
}
string key = thisLine[0] + "_" + (i == 0 ? "Call" : "Put") + "_" + K.ToString();
addData(tick, key, ref tickData, ref ratesData, ref addRow); // Передаем в лямбду на заполнение структуры
QuotesDetales detales_current = new QuotesDetales
{
assetName = key,
isCall = (i == 0 ? true : false),
strike = K
};
if (fileMonth.CompareTo(expiryMonth) >= 0 &&
expiredAssets.FindAll(x => x.assetName == detales_current.assetName).Count == 0)
expiredAssets.Add(detales_current);
}
}
}
}
}
}
}
return expiredAssets;
}
private void getStrike(double Price, double Step, int num, ref List<double> ans)
{
// Получаем список Double который далее будем перебирать
}
private void fileWriter(ref SortedList<string, List<MqlTick>> tickData,
ref SortedList<string, List<MqlRates>> ratesData,
ref List<QuotesDetales> assetsToWrite,
DateTime endDate,
string pathToFolder)
{
WriteConverter<MqlTick> writeData_tick = (MqlTick tick) =>
{
// сткукрута в строку
};
WriteConverter<MqlRates> writeData_rates = (MqlRates rate) =>
{
// сткукрута в строку
};
for (int i = 0; i < assetsToWrite.Count; i++)
{
QuotesDetales item = assetsToWrite[i];
item.expiration = tickData[item.assetName].Max(x => x.time);
assetsToWrite[i] = item;
Writer(pathToFolder + "\\TickData\\" + item.assetName + ".csv", tickData[item.assetName], writeData_tick, "<DATE>\t<TIME>\t<BID>\t<ASK>\t<LAST>\t<VOLUME>");
tickData.Remove(item.assetName);
Writer(pathToFolder + "\\CandleData\\" + item.assetName + ".csv", ratesData[item.assetName], writeData_rates, "<DATE>\t<TIME>\t<OPEN>\t<HIGH>\t<LOW>\t<CLOSE>\t<TICKVOL>\t<VOL>\t<SPREAD>");
ratesData.Remove(item.assetName);
}
}
private void Writer<T>(string path, List<T> Data, WriteConverter<T> converter, string headdder)
{
// пишу в файл
}
Я переписал этот код следующим образом, проблема с памятью вроде исчезла, но на что на что у первого варианта уходил час - два, у второго уходит пол дня... И я не пойму от чего так.
Если в вкратце, то переписанный вариант, не накапливает создаваемые мною файлы, а накапливает только лишь исходную информацию для создания файлов, ее намного меньше и памяти занимает она тоже меньше. Затем при наступлении определенного условия, разом создаю требуемые списки и сохраняю их в файлы, далее память отчищается так как эти списки хранятся в локальных переменных. функции "fileWriter" Код ниже:
public delegate double ToDouble(string num);
delegate void DataManager(MqlTick tick,
string key,
ref Dictionary<string, List<MqlTick>> tickData,
ref Dictionary<string, List<MqlRates>> ratesData,
ref bool addRow,
ref DateTime DT);
delegate double SmileToPrice(smileData IV_data, double strike, bool isCall);
delegate void CheckDirectories(string pathToFolder, string dirName);
delegate string WriteConverter<T>(T Data);
class Converter
{
public Converter(HistoryCreator mainWindow)
{
this.mainWindow = mainWindow;
max = 0;
fileName = "";
provider = new NumberFormatInfo();
provider.NumberDecimalSeparator = ".";
cond = mainWindow.showReadingLine;
}
HistoryCreator mainWindow;
int max;
string fileName;
private NumberFormatInfo provider;
private bool cond;
public void createQuotes(inputData data)
{
// запускаем создание файлов
}
private DateTime getExpiryMonth(string futName)
{
// Получаем дату из переданой строки
}
private List<string> fileReader(ref Dictionary<string, List<smileData>> smileTickData,
inputData data,
string selectedFile,
DateTime fileMonth)
{
List<string> expiredAssets // устанавливаю
// Select asset by dates
DateTime start // устанавливаю
DateTime end // устанавливаю
// String to double
ToDouble strToDouble = (string num) =>
{
// Преобразовываем число из строки в double (часть файлов с запятыми, часть с точками ввиде разделителя идут)
};
// reading file
using (StreamReader reader = File.OpenText(selectedFile))
{
mainWindow.Invoke(mainWindow.pbUpdater, new object[] { false, max, fileName, "Чтение файла", true });
reader.ReadLine();
string line;
int c = 1;
while ((line = reader.ReadLine()) != null)
{
if (mainWindow.showReadingLine)
{
if (!cond) cond = true;
mainWindow.Invoke(mainWindow.pbUpdater, new object[] { false, max, fileName, "Чтение файла Строка №" + c.ToString(), false });
c++;
}
else if (cond)
{
cond = false;
mainWindow.Invoke(mainWindow.pbUpdater, new object[] { false, max, fileName, "Чтение файла", false });
}
if (!string.IsNullOrEmpty(line) && !string.IsNullOrWhiteSpace(line))
{
string[] thisLine = line.Split(';');
DateTime expiryMonth = getExpiryMonth(thisLine[0]);
if (thisLine[0].Substring(0, data.mask.Length).CompareTo(data.mask) == 0 &&
expiryMonth.CompareTo(start) >= 0 && expiryMonth.CompareTo(end) <= 0)
{
DateTime currentDT = DateTime.ParseExact(thisLine[2], "yyyyMMddHHmmssfff", null);
if (strToDouble(thisLine[4]) != 0 &&
strToDouble(thisLine[10]) > 0 &&
currentDT.CompareTo(data.from) >= 0)
{
smileData IV_data = new smileData
{
Price = strToDouble(thisLine[3]),
A = strToDouble(thisLine[5]),
B = strToDouble(thisLine[6]),
C = strToDouble(thisLine[7]),
D = strToDouble(thisLine[8]),
E = strToDouble(thisLine[9]),
S = strToDouble(thisLine[4]),
T = strToDouble(thisLine[10]),
currentDT = currentDT
};
if (!smileTickData.ContainsKey(thisLine[0]))
smileTickData.Add(thisLine[0], new List<smileData>());
smileTickData[thisLine[0]].Add(IV_data);
if (fileMonth.CompareTo(expiryMonth) >= 0 &&
!expiredAssets.Contains(thisLine[0]))
expiredAssets.Add(thisLine[0]);
}
}
}
}
}
return expiredAssets;
}
private void getStrike(double Price, double Step, int num, ref List<double> ans)
{
// Получаем список Double который далее будем перебирать
}
private List<QuotesDetales> fileWriter(ref Dictionary<string, List<smileData>> smileTickData,
List<string> assetsToWrite,
inputData data,
ref DateTime DT)
{
string pathToFolder = data.pathToFolder + "\\Result\\" + data.resultDirName;
List<QuotesDetales> fileDetales = new List<QuotesDetales>();
WriteConverter<MqlTick> writeData_tick = (MqlTick tick) =>
{
// структура в строку
};
WriteConverter<MqlRates> writeData_rates = (MqlRates rate) =>
{
// структура в строку
};
WriteConverter<smileData> writeData_smileParams = (smileData smile) =>
{
// структура в строку
};
SmileToPrice getPrice = (smileData IV_data, double strike, bool isCall) =>
{
// Считаем значение цены
};
// Add data
DataManager addData = (MqlTick tick,
string key,
ref Dictionary<string, List<MqlTick>> ticks,
ref Dictionary<string, List<MqlRates>> rates,
ref bool addRow,
ref DateTime DT_border) =>
{
// Заполняем tickData и rateData (переданые по ссылкам как ticks и rates)
};
mainWindow.Invoke(mainWindow.pbUpdater, new object[] { false, max, fileName, "Создание котировок", false });
if (assetsToWrite.Count > 0)
{
foreach (string key in assetsToWrite)
{
List<double> strikes = new List<double>();
Dictionary<string, List<MqlTick>> tickData = new Dictionary<string, List<MqlTick>>(); // Времено созданые списки для будующих файлов
Dictionary<string, List<MqlRates>> rateData = new Dictionary<string, List<MqlRates>>(); // Времено созданые списки для будующих файлов
List<QuotesDetales> current_fileDetales = new List<QuotesDetales>(); // Времено созданые списки для будующих файлов
int c = 0;
int total = smileTickData[key].Count;
while (c < total)
{
smileData item = smileTickData[key][c];
getStrike(item.Price, data.stepStrikes, data.numStrikes, ref strikes);
c++;
MqlTick tick = new MqlTick
{
time = item.currentDT,
volume = 0
};
bool addRow = false;
for (int j = 0; j < strikes.Count; j++)
{
double K = strikes[j];
for (int i = 0; i < 2; i++)
{
tick.last = getPrice(item, K, (i == 0 ? true : false));
tick.bid = tick.last;
tick.ask = tick.last;
if (data.spreadBidAsk > 0)
{
// Делаем еще некоторые действия с преобразованием цены
}
string currentFile_name = key + "_" + (i == 0 ? "Call" : "Put") + "_" + K.ToString();
if (mainWindow.showReadingLine)
{
if (!cond) cond = true;
mainWindow.Invoke(mainWindow.pbUpdater, new object[] { false, max, fileName, (c + 1).ToString() + " х " + total.ToString() + " | " + currentFile_name, false });
}
else if (cond)
{
cond = false;
mainWindow.Invoke(mainWindow.pbUpdater, new object[] { false, max, fileName, "Создание котировок", false });
}
addData(tick, currentFile_name, ref tickData, ref rateData, ref addRow, ref DT);
QuotesDetales detales_current = new QuotesDetales
{
assetName = currentFile_name,
isCall = (i == 0 ? true : false),
strike = K,
expiration = smileTickData[key].Max(x => x.currentDT)
};
if (current_fileDetales.FindAll(x => x.assetName.CompareTo(detales_current.assetName) == 0).Count == 0)
current_fileDetales.Add(detales_current);
}
}
}
mainWindow.Invoke(mainWindow.pbUpdater, new object[] { false, max, fileName, "Сохранение данных", false });
for (int i = 0; i < current_fileDetales.Count; i++)
{
QuotesDetales item = current_fileDetales[i];
Writer(pathToFolder + "\\TickData\\" + item.assetName + ".csv", tickData[item.assetName], writeData_tick, "<DATE>\t<TIME>\t<BID>\t<ASK>\t<LAST>\t<VOLUME>");
Writer(pathToFolder + "\\CandleData\\" + item.assetName + ".csv", rateData[item.assetName], writeData_rates, "<DATE>\t<TIME>\t<OPEN>\t<HIGH>\t<LOW>\t<CLOSE>\t<TICKVOL>\t<VOL>\t<SPREAD>");
Writer(pathToFolder + "\\SmileParamsTickData\\" + key + ".csv", smileTickData[key], writeData_smileParams, "DT Unix miliseconds;Price;S;A;B;C;D;E;T");
}
fileDetales.AddRange(current_fileDetales);
smileTickData.Remove(key);
}
}
return fileDetales;
}
private void Writer<T>(string path, List<T> Data, WriteConverter<T> converter, string headdder)
{
// пишу в файл
}
}
Как видно код создания файлов сохранения и прочего не поменялся, я всего лишь поменял подход к формированию информации. Однако скорость снизилась в разы ! от чего так получилось ? буду благодарен если поможете понять данное явление. Понятно что количество проходов по строкам исходного файла возросло в два раза (сначала читаю, сохраняю, затем перечитываю, формирую и сохраняю в файл) однако скорость больше чем в два раза снизилась !
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
В axml описана кнопка, по клику на кнопку, нужно открыть галерею и передать в ViewModel путь к выбранному изображениюРеализация в рамках MVVMCross, нужен...