Как улучшить эффективность цикла - PullRequest
3 голосов
/ 02 марта 2020

Я пытаюсь сохранить свои данные в CSV-файл. Когда скорость мала (2000 / с, чтобы сохранить), она работает хорошо. Но при увеличении до 20000 / с оно работает медленно.

class Channel 
{
    List<double> RawData { get; set; }
    ...
}

-----------------------------------

var channels = new List<Channel>();
// after fetch the data
var sw = new StreamWriter(FileStream, Encoding.Default);
for (i = 0; channels.First().RawData.Count; i ++)
{
    string line = DateTime.Now.ToString() + ",";
    line += string.Join(',', channels.Select(c => c.RawData[i]));
    sw.WriteLine(line);
    sw.Flush();
}

Когда число RawData каждого channel достигает 20000, приложение будет работать медленно. Есть ли какое-нибудь решение для ускорения генерации line?

Ответы [ 2 ]

4 голосов
/ 02 марта 2020

Есть несколько вещей, которые вы можете сделать, чтобы ускорить это.

Как правило, операции ввода-вывода являются дорогими (медленными), поэтому старайтесь избегать выполнения многих из них, а вместо этого пишите только один или несколько раз.

Исправление строк также происходит медленно, если вы делаете это много раз. Поэтому вместо этого вы должны использовать StringBuilder.

И, наконец, мне кажется, что дата и время, которые вы используете, во многих случаях (но, возможно, не во всех!) Будут иметь одинаковое значение. Если это так, то вы можете вызвать это только один раз, а затем вывести то же значение.

Вы также можете ускорить оператор выбора каналов (как в других сообщениях).

Тогда у вас будет такой код:

StringBuilder sb = new StringBuilder();
string dtValue = DateTime.Now.ToString();

for (int i = 0; channels.First().RawData.Count; i++)
{
    sb.Append(dtValue).Append(",").Append(channels.First().RawData[i]).Append(Environment.NewLine);    
}

//Write just once.
using (var sw = new StreamWriter(FileStream, Encoding.Default))
{
    sw.Writeline(sb.ToString());
    sw.Close();
}

Редактировать: обновлен для использования channel.First (). RawData [i], согласно комментариям.

1 голос
/ 02 марта 2020

Вы можете использовать Write (string) метод и не объединять строки.

Также * метод 1005 * не должен вызываться для каждой итерации.

for (i = 0; channels.First().RawData.Count; i ++) не компилируется и не должен использоваться, поскольку string.Join(',', channels.Select(c => c.RawData[i])); будет выполнять итерацию по всем элементам.

Пример кода ниже не выполняет конкатенацию и не читает все данные в памяти. Он записывает данные канала в поток для каждого канала отдельно. Конечно, это займет некоторое время, и чем больше данных у вас будет, тем дольше они будут работать.

var channels = new List<Channel>();
// after fetch the data
using (var sw = new StreamWriter(FileStream, Encoding.Default)) // you have to check that both stream writer and `FileStream` instances are disposed properly 
{
foreach (var ch in channels)
{
sw.Write(string.Format("{0:ddMMyyyy hh:mm:ss},", DateTime.Now)); // it writes date time for every channel data 
foreach (var data in ch.RawData)
{
    sw.Write(string.Format("{0},", data.ToString(Culture.InvariantCulture)); // double.ToString() is culture specific so you can use Culture.InvariantCulture when converting double to string
}
sw.WriteLine(""); // last line break but note that all channel data are written in one line separated with commas
}
}

Основная идея состоит не в том, чтобы объединить все каналы списка элементов RawData в строку. Определенно потребуется дополнительное время и память, чтобы преобразовать 20000 двойных чисел в одну строку.

Он просто записывает элементы RawData по одному в файл и экономит время и память (надеюсь)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...