Переформатирование текста постепенно замедляется с каждой итерацией - PullRequest
1 голос
/ 10 февраля 2011

РЕДАКТИРОВАТЬ 2

Хорошо, я разместил копию моего источника на gist.github , и у меня есть только одна давняя проблема, которую я не могу решить.

FindLine() всегда возвращает -1. Я сузил причину до утверждения if, но не могу понять почему. Я знаю, что и Symbol, и SymbolList получают хорошие данные.

/ РЕДАКТ. 2

У меня есть довольно простая программа на C #, которая ищет файл .csv, считывает текст в этом файле, форматирует его (и включает некоторую информацию из запроса SQL, загруженного в DataTable) и сохраняет его в файле .tsv. для последующего использования другой программой.

Моя проблема в том, что иногда исходный файл .csv занимает более 10000 строк, и программа постепенно замедляется по мере итерации по строкам. Если размер файла .csv составляет ~ 500 строк, его выполнение занимает около 45 секунд, и на этот раз экспоненциально ухудшается с увеличением размера файла .csv.

SQL-запрос возвращает более 37 000 строк, но запрашивается только один раз и сортируется так же, как и файл .csv, поэтому обычно я не замечаю, что он проходит через этот файл, если не может найти соответствующие данные, в этом случае он проходит весь путь и возвращает соответствующий текст ошибки. Я на 99% уверен, что это не является причиной замедления.

Циклы y и z должны быть точно такими же, как и они.

Если это абсолютно необходимо, я могу почистить некоторые данные из исходного файла .csv и опубликовать пример, но я действительно надеюсь, что мне просто не хватает чего-то действительно очевидного.

Заранее спасибо, ребята!

Вот мой источник:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;

namespace MoxySectorFormatter
{
    class Program
    {
        static void Main(string[] args)
        {
            DataTable resultTable = new DataTable();
            double curLine = 1;
            double numLines = 0;
            string ExportPath = @"***PATH***\***OUTFILE***.tsv";
            string ImportPath = @"***PATH***\***INFILE***.csv";
            string NewText = "SECURITY\r\n";
            string OrigText = "";
            string QueryString = "SELECT DISTINCT UPPER(MP.Symbol) AS Symbol, LOWER(MP.SecType) AS SecType, MBI.Status FROM MoxySecMaster AS MP LEFT JOIN MoxyBondInfo AS MBI ON MP.Symbol = MBI.Symbol AND MP.SecType = MBI.SecType WHERE MP.SecType <> 'caus' AND MP.SecType IS NOT NULL AND MP.Symbol IS NOT NULL ORDER BY Symbol ASC;";
            SqlConnection MoxyConn = new SqlConnection("server=***;database=***;user id=***;password=***");
            SqlDataAdapter adapter = new SqlDataAdapter(QueryString, MoxyConn);

            MoxyConn.Open();
            Console.Write("Importing source file from \"{0}\".", ImportPath);
            OrigText = File.ReadAllText(ImportPath);
            OrigText = OrigText.Substring(OrigText.IndexOf("\r\n", 0) + 2);
            Console.WriteLine("\rImporting source file from \"{0}\".  Done!", ImportPath);
            Console.Write("Scanning source report.");
            for (int loop = 0; loop < OrigText.Length; loop++)
            {
                if (OrigText[loop] == '\r')
                    numLines++;
            }
            Console.WriteLine("\rScanning source report.  Done!");
            Console.Write("Downloading SecType information.");
            resultTable = new DataTable();
            adapter.Fill(resultTable);
            MoxyConn.Close();
            Console.WriteLine("\rDownloading SecType information.  Done!");

            for (int lcv = 0; lcv < numLines; lcv++)
            {
                int foundSpot = -1;
                int nextStart = 0;
                Console.Write("\rGenerating new file... {0} / {1} ({2}%)  ", curLine, numLines, System.Math.Round(((curLine / numLines) * 100), 2));
                for (int vcl = 0; vcl < resultTable.Rows.Count; vcl++)
                {
                    if (resultTable.Rows[vcl][0].ToString() == OrigText.Substring(0, OrigText.IndexOf(",", 0)).ToUpper() && resultTable.Rows[vcl][1].ToString().Length > 0)
                    {
                        foundSpot = vcl;
                        break;
                    }
                }
                if (foundSpot != -1 && foundSpot < resultTable.Rows.Count)
                {
                    NewText += resultTable.Rows[foundSpot][1].ToString();
                    NewText += "\t";
                    NewText += OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart));
                    NewText += "\t";
                    nextStart = OrigText.IndexOf(",", nextStart) + 1;
                    for (int y = 0; y < 142; y++)
                        NewText += "\t";
                    if(resultTable.Rows[foundSpot][2].ToString() == "r")
                        NewText += @"PRE/ETM";
                    else if (OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)) == "Municipals")
                    {
                        NewText += "Muni - ";
                        nextStart = OrigText.IndexOf(",", nextStart) + 1;
                        if (OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)).Length > 0)
                            NewText += OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart));
                        else
                            NewText += "(Orphan)";
                    }
                    else if (OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)) == "Corporates")
                    {
                        NewText += "Corporate - ";
                        nextStart = OrigText.IndexOf(",", nextStart) + 1;
                        nextStart = OrigText.IndexOf(",", nextStart) + 1;
                        if (OrigText.Substring(nextStart, (OrigText.IndexOf("\r\n", nextStart) - nextStart)).Length > 0)
                            NewText += OrigText.Substring(nextStart, (OrigText.IndexOf("\r\n", nextStart) - nextStart));
                        else
                            NewText += "(Unknown)";
                    }
                    else
                        NewText += OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart));
                    for (int z = 0; z < 17; z++)
                        NewText += "\t";
                    NewText += "\r\n";
                    resultTable.Rows.RemoveAt(foundSpot);
                }
                else
                    Console.WriteLine("\r  Omitting {0}: Missing Symbol or SecType.", OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)));
                OrigText = OrigText.Substring(OrigText.IndexOf("\r\n", 0) + 2);
                curLine++;
            }
            Console.Write("Exporting file to \"{0}\".", ExportPath);
            File.WriteAllText(ExportPath, NewText);
            Console.WriteLine("\rExporting file to \"{0}\".  Done!\nPress any key to exit.", ExportPath);
            Console.ReadLine();
        }
    }
}

Ответы [ 4 ]

8 голосов
/ 10 февраля 2011

Вместо использования оператора + = для объединения, используйте объект System.Text.StringBuilder и его методы Append () и AppendLine

Строки являются неизменяемыми в C #, поэтому каждый раз, когда вы используете + = вЦикл, новая строка создается в памяти, что может привести к замедлению.

1 голос
/ 11 февраля 2011

NewText только добавляется, верно? Так почему бы просто не записать в файл поток? Также не забудьте добавить пробную зацепку, так что если ваше приложение взорвется, вы можете закрыть поток файлов.

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

string txt = OrigText.Substring(0, OrigText.IndexOf(",", 0)).ToUpper()
for (int vcl = 0; vcl < resultTable.Rows.Count; vcl++)
{
  if (resultTable.Rows[vcl][0].ToString() == txt && resultTable.Rows[vcl][1].ToString().Length > 0)
  {
      foundSpot = vcl;
      break;
  }
}

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

string tab17 = "\t\t\t\t\t\t\t\t\t"
string tab142 = "\t\t\t\t\t...etc." 

//bad
for (int z = 0; z < 17; z++)
  NewText += "\t";
1 голос
/ 11 февраля 2011

В то время как это взывает к StringBuilder, я не верю, что это основной виновник здесь. Скорее, у нас есть кусок кода с экспоненциальным временем выполнения.

Виновник, который я имею в виду, - это код, который вычисляет foundSpot. Если я правильно читаю код, это O (n ^ 2), а все остальное - O (n).

Три совета:

1) Рефакторинг! Эта процедура WAY слишком длинная. Мне не нужно ссылаться на «код, который вычисляет foundSpot», это должна быть процедура с именем. Я вижу здесь минимум 4 процедуры, а может и больше.

2) Stringbuilder.

3) Эта процедура поиска должна быть очищена. Вы выполняете много повторных вычислений каждый раз в течение цикла, и если нет какой-либо причины против этого (я не буду пытаться выяснить, какие тесты вы применяете), это нужно сделать с чем-то, что повышает эффективность поиска, чем О (п).

1 голос
/ 10 февраля 2011

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

Каждый раз, когда программадобавляет что-то к концу выходной строки, C # создает новую строку, которая достаточно велика для старой строки плюс добавленный текст, копирует старое содержимое в целевую строку, а затем добавляет новый текст в конец.

Предполагая, что 40 символов в строке и 500 строк, общий размер строки будет ~ 20 КБ, и в этот момент накладные расходы всех этих 20 КБ копий замедляют работу программы.

...