Я «унаследовал» некоторое устаревшее программное обеспечение для обработки больших текстовых файлов, которое в настоящее время написано на Visual Foxpro (специально для версии 7).Производительность C#/SQLite против FoxPro для импорта файлов
Я изучаю варианты замены этих программ на что-то новое, которое будет легче модифицировать и поддерживать. Тем не менее, я не могу приблизиться к производительности Foxpro для импорта больших текстовых файлов.
Например, у меня есть текстовый файл ~ 1gb с ~ 110 000 записей, каждый из которых имеет 213 полей (каждая строка имеет длину 9247 символов), с которыми я тестирую.
Использование Foxpro:
? SECONDS()
USE new
APPEND FROM test.dat TYPE sdf
USE
? SECONDS()
Этот импорт в базу данных завершена всего за 10 секунд на моем компьютере.
Использование C#/SQLite (с Filehelpers и System.Data.SQLite), самый быстрый, который я смог получить, чтобы завершить этот импорт, составляет более минуты. Я попытался оптимизировать это как можно лучше, используя предложения от this question (например, транзакции и т. Д.). Реально минуту для импорта файла 1gb не кажется плохим, если я не сравнивал его с 10 секундами для Foxpro.
Примерная разбивка времени, затраченного в течение ~ 1 минуты:
Filehelpers reading file and parsing: 12 seconds.
Building SQL commands from Filehelpers object: 15 seconds.
Running individual ExecuteNonQuery()'s: 24 seconds.
Committing transactions (every 5000 records): 12 seconds.
По сравнению с нитью связана моими вставками в секунду гораздо медленнее, но мои записи имеют 213 полеев против 7, так это, как ожидается. Если я сломаю его полями/секундами, я получаю примерно 360 000 против 630 000 потоков. Скорость вставки по мегабайту составляет ~ 2,24 мегабайта/с для другого плаката и 15,4 мегабайта/с для меня. Поэтому я думаю, что моя производительность сопоставима с другим плакатом, и, вероятно, не будет больше оптимизации, которую я могу сделать.
Почему это так медленнее, чем импорт Foxpro (на 5-6x медленнее)? Является ли это просто яблоками для апельсинов, и я должен просто принять более медленную скорость в компромиссе для других преимуществ, которые получаю благодаря использованию более новых технологий?
EDIT:
Вот некоторый тестовый код я столкнулся со случайными данными, показывая аналогичные скорости .... я осуществление сделок правильно? ExecuteNonQuery() занимает значительное количество времени. Я реализовал все команды SQL с простой, не параметризованной, скорее всего, неэффективной манипуляцией строками, но на данный момент я не слишком озабочен этим (я буду читать из файлов, и этот метод был намного быстрее, чем использование сущности framework).
public class Program
{
public static void Main(string[] args)
{
TestSqlite(100, 20000);
}
public static void TestSqlite(int numColumns, int numRows)
{
Console.WriteLine("Starting Import.... columns: " + numColumns.ToString() + " rows: " + numRows.ToString());
var conn = new SQLiteConnection(@"Data Source=C:\vsdev\SqliteTest\src\SqliteTest\ddic.db;Version=3");
conn.Open();
var cmd = new SQLiteCommand(conn);
cmd.CommandText = "DROP TABLE IF EXISTS test";
cmd.ExecuteNonQuery();
string createCmd = "CREATE TABLE 'test'('testId' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ";
for (var i = 0; i < numColumns; i++)
{
createCmd += "'test" + i.ToString() + "' TEXT, ";
}
createCmd = createCmd.Substring(0, createCmd.Length - 2);
createCmd += ")";
cmd.CommandText = createCmd;
cmd.ExecuteNonQuery();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var transaction = conn.BeginTransaction();
int lineCount = 0;
long startTime;
long endTime;
long totalStringTime = 0;
long totalAddTime = 0;
long totalSaveTime = 0;
string command;
for (var l = 0; l < numRows; l++)
{
startTime = stopWatch.ElapsedMilliseconds;
command = CreateRandomInsert("test", numColumns);
endTime = stopWatch.ElapsedMilliseconds;
totalStringTime += endTime - startTime;
///Execute Query
startTime = stopWatch.ElapsedMilliseconds;
cmd.CommandText = command;
cmd.ExecuteNonQuery();
endTime = stopWatch.ElapsedMilliseconds;
totalAddTime += endTime - startTime;
if (lineCount > 5000)
{
lineCount = 0;
startTime = stopWatch.ElapsedMilliseconds;
transaction.Commit();
transaction.Dispose();
transaction = conn.BeginTransaction();
cmd = new SQLiteCommand(conn);
endTime = stopWatch.ElapsedMilliseconds;
totalSaveTime += endTime - startTime;
Console.Write('.');
}
lineCount += 1;
}
startTime = stopWatch.ElapsedMilliseconds;
transaction.Commit();
transaction.Dispose();
endTime = stopWatch.ElapsedMilliseconds;
totalSaveTime += endTime - startTime;
Console.WriteLine('.');
Console.WriteLine("String time: " + totalStringTime.ToString());
Console.WriteLine("ExecuteNonQuery time: " + totalAddTime.ToString() + ", per 1000 records: " + (totalAddTime/(numRows/1000)).ToString());
Console.WriteLine("Commit time: " + totalSaveTime.ToString() + ", per 1000 records: " + (totalSaveTime/(numRows/1000)).ToString());
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds/10);
Console.WriteLine(" in " + elapsedTime);
conn.Close();
}
public static string CreateRandomInsert(string TableName, int numColumns)
{
List<string> nameList = new List<string>();
List<string> valueList = new List<string>();
for (var i = 0; i < numColumns; i++)
{
nameList.Add("test" + i.ToString());
valueList.Add(Guid.NewGuid().ToString());
}
return CreateSql(TableName, nameList, valueList);
}
public static string CreateSql(string TableName, List<string> names, List<string> values)
{
string textCommand = "";
textCommand += "INSERT INTO " + TableName + " (";
foreach (var nameVal in names)
{
textCommand += nameVal + ", ";
}
textCommand = textCommand.Substring(0, textCommand.Length - 2);
textCommand += ") VALUES (";
foreach (var val in values)
{
textCommand += "'" + val + "', ";
}
textCommand = textCommand.Substring(0, textCommand.Length - 2);
textCommand += ");";
return textCommand;
}
}
Следует попробовать [это] (http://stackoverflow.com/a/759866/4937064)? – mimimito
К сожалению, мой файл имеет фиксированную ширину поля, возможно, я перейду в CSV и попробую прямой импорт для проверки производительности. – playsted