Основная c ошибка, которую вы делаете, это только 1 одна строка на кадр , поэтому вы можете в основном рассчитать, сколько времени у вас займет около 60 кадров в секунду:
120,000 rows / 60fps = 2000 seconds = 33.3333 minutes
из-за на yield return null;
, который в основном говорит: «Приостановите процедуру, визуализируйте этот кадр и продолжите в следующем кадре».
Конечно, было бы намного быстрее говорить об абсолютном времени, не используя yield return null
или Coroutine вообще, но пусть все это будет проанализировано в одном go. Но затем, конечно, он на мгновение замораживает основной поток пользовательского интерфейса.
Чтобы избежать этого, лучший способ, на мой взгляд, на самом деле будет заключаться в перемещении всего объекта в Thread
/ Task
и вернуть только результат!
FileIO и синтаксический анализ строк всегда довольно медленный.
Однако, я думаю, вы уже могли бы значительно ускорить его, просто используя StopWatch
как
...
var stopWatch = new Stopwatch();
stopWatch.Start();
// Use the last frame duration as a guide for how long one frame should take
var targetMilliseconds = Time.deltaTime * 1000f;
while ((line = sr.ReadLine()) != null)
{
....
// If you are too long in this frame render one and continue in the next frame
// otherwise keep going with the next line
if(stopWatch.ElapsedMilliseconds > targetMilliseconds)
{
yield return null;
stopWatch.Restart();
}
}
Это позволяет обрабатывать несколько строк в одном кадре, пытаясь сохранить частоту кадров 60 кадров в секунду. Вы можете немного поэкспериментировать с ним, чтобы найти хороший компромисс между частотой кадров и продолжительностью. Например, возможно, вы можете позволить ему работать только со скоростью 30 кадров в секунду, но импортировать быстрее, поскольку таким образом он может обрабатывать больше строк в одном кадре.
В общем, я бы не стал читать «вручную» через каждый байт / символ. Вместо этого используйте для этого встроенные методы, например, String.Split
.
На самом деле я использую немного более продвинутый Regex.Matches
, поскольку при экспорте CSV из Excel возможны особые случаи, такие как одна ячейка, содержащая ,
или другие специальные символы, например разрывы строк (!).
В этом случае Excel делает это, заключая ячейку в "
. Это добавляет второй особый случай, а именно ячейку, содержащую "
.
Regex.Marches
, конечно, довольно сложный и медленный, но охватывает эти особые случаи. (См. Также Basi c правила CSV для более подробного объяснения особых случаев)
Если вы хорошо знаете формат вашего CSV и не нуждаетесь в нем, вы могли бы / должны, вероятно, скорее просто придерживайтесь
var columns = row.Split(new []{ ','});
, чтобы всегда разделять его только на ,
, что будет работать быстрее.
private const char Quote = '\"';
private const string LineBreak = "\r\n";
private const string DoubleQuote = "\"\"";
private IEnumerator readDataset(string path)
{
starsRead = 0;
// Use the last frame duration as a guide how long one frame should take
// you can also try and experiment with hardcodd target framerates like e.g. "1000f / 30" for 30fps
var targetMilliseconds = Time.deltaTime * 1000f;
var stopWatch = new Stopwatch();
// NOTE: YOU ARE ALREADY READING THE ENTIRE FILE HERE ONCE!!
// => Instead of later again read it line by line rather re-use this file content
var lines = File.ReadLines(path).ToArray();
var totalLines = lines.Length;
totalStars = totalLines - 1;
// HERE YOU DID READ THE FILE AGAIN JUST TO GET THE FIRST LINE ;)
string firstLine = lines[0];
var firstLineColumns = GetColumns(firstLine);
columnCount = firstLineColumns.Length;
var datasetTable = new string[totalStars, columnCount];
stopWatch.Start();
for(var i = 0; i < totalStars; i++)
{
string row = lines[i + 1];
string[] columns = GetColumns(row);
var colIndex = 0;
foreach(var column in columns)
{
if(colIndex >= columnCount - 1) break;
datasetTable[i, colIndex] = colum;
colIndex++;
}
starsRead = i + 1;
// If you are too long in this frame render one and continue in the next frame
// otherwise keep going with the next line
if (stopWatch.ElapsedMilliseconds > targetMilliseconds)
{
yield return null;
stopWatch.Restart();
}
}
}
private string[] GetColumns(string row)
{
var columns = new List<string>();
// Look for the following expressions:
// (?<x>(?=[,\r\n]+)) --> Creates a Match Group (?<x>...) of every expression it finds before a , a \r or a \n (?=[...])
// OR |
// ""(?<x>([^""]|"""")+)"" --> An Expression wrapped in single-quotes (escaped by "") is matched into a Match Group that is neither NOT a single-quote [^""] or is a double-quote
// OR |
// (?<x>[^,\r\n]+)),?) --> Creates a Match Group (?<x>...) that does not contain , \r, or \n
var matches = Regex.Matches(row, @"(((?<x>(?=[,\r\n]+))|""(?<x>([^""]|"""")+)""|(?<x>[^,\r\n]+)),?)", RegexOptions.ExplicitCapture);
foreach (Match match in matches)
{
var cleanedMatch = match.Groups[1].Value == "\"\"" ? "" : match.Groups[1].Value.Replace("\"\"", Quote.ToString());
columns.Add(cleanedMatch);
}
// If last thing is a `,` then there is an empty item missing at the end
if (row.Length > 0 && row[row.Length - 1].Equals(','))
{
columns.Add("");
}
return columns.ToArray();
}