编辑2
好的,我在gist.github发布了我的来源副本,而且我只有一个我无法解决的问题。
FindLine()
始终返回-1。我已将原因缩小到if语句,但我无法弄清楚原因。我知道symbol和symbolList都传递了良好的数据。
/编辑2
我有一个相当简单的C#程序,它查找.csv文件,读取该文件中的文本,重新格式化它(并包含加载到DataTable中的SQL查询中的一些信息),并将其保存到.tsv文件中以供其他程序使用。
我的问题是,有时源.csv文件超过10,000行,程序在遍历行时逐渐减慢。如果.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();
}
}
}
答案 0 :(得分:8)
不使用+ =运算符进行连接,而是使用System.Text.StringBuilder对象,它的Append()和AppendLine方法
字符串在C#中是不可变的,因此每次在循环中使用+ =时,都会在内存中创建一个新字符串,这可能会导致最终的减速。
答案 1 :(得分:1)
您应该在创建输出文件时将每一行写入输出文件,而不是将所有行附加到输出字符串的末尾(NewText)并在结尾处写出来。
每次程序将某些内容添加到输出字符串的末尾时,C#就会创建一个新字符串,该字符串对于旧字符串加上附加文本足够大,将旧内容复制到目标字符串中,然后将新文本附加到结束。
假设每行40个字符和500行,总字符串大小将为~20K,此时所有这些20K副本的开销正在减慢程序WAY的速度。
答案 2 :(得分:1)
虽然这引起了StringBuilder的呼唤,但我不认为这是罪魁祸首。相反,我们有一段带有指数运行时的代码。
我想到的罪魁祸首是计算findSpot的代码。如果我正确地读取代码,那么这是O(n ^ 2),而其他一切都是O(n)。
三条建议:
1)重构!这个例程 WAY 太长了。我不应该引用“计算findSpot的代码”,这应该是一个带有名称的例程。我在这里看到至少4个例程,可能更多。
2)Stringbuilder。
3)必须清理搜索程序。你每次循环都会做很多重复的计算,除非有一些理由反对它(我不打算试图找出你正在应用的测试),它需要用搜索性能优于为O(n)。
答案 3 :(得分:1)
NewText仅附加到,对吧?那么为什么不写出文件流呢?另外,不要忘记在其周围添加try catch,因此如果您的应用程序爆炸,您可以关闭文件流。
另外,如果拔出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";