2016-01-07 2 views
1

Я «унаследовал» некоторое устаревшее программное обеспечение для обработки больших текстовых файлов, которое в настоящее время написано на 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; 
    } 
} 
+0

Следует попробовать [это] (http://stackoverflow.com/a/759866/4937064)? – mimimito

+0

К сожалению, мой файл имеет фиксированную ширину поля, возможно, я перейду в CSV и попробую прямой импорт для проверки производительности. – playsted

ответ

0

Когда данные фиксированной ширины, VFP имеет преимущество перед другими базами данных. Низкий уровень, это почти тот же формат, что VFP хранит свои данные. Например, если у вас нет каких-либо полей, которые несовместимы с Foxpro 2.x, то импорт этих данных будет просто означать добавление 0x20 байт перед каждой строкой (+ запись заголовка файла, если он еще не был написан) - и обновление индексов, если таковые имеются, это будет часть времени, требующая много времени. Если вам нужно разбирать строки, то VFP довольно медленный по строковым операциям, по сравнению с C#.

(я не знаю FileHelpers, спасибо за наведение на него)

9247 байт фиксированных данных и 213 полей интересно. Я хотел бы протестировать вставку в SQLite, postgreSQL, SQL-сервер и некоторые базы данных NoSQL. Не могли бы вы поделиться пустой dbf (или просто структурой как код или xml - может быть, с 1-2 строками выборочных данных из файла)?Я не пробовал раньше с 200 + полями, в противном случае 1 минута для строк 110K будет медленным.

Таблица VFP может добавлять такие данные только 2 раза, и вам нужно будет создать другую таблицу. Вы говорите «манипулирование» текстовыми файлами, но вы не прокомментировали, что такое манипуляция. Может быть, прямая «манипуляция» без добавления к таблице более эффективна и быстрее, а весь процесс завершается быстрее? Просто мысль.

+0

Большая информация Цетин. Я подумал, что должно быть что-то принципиально проще в том, что делал Foxpro. Я попытаюсь собрать образец файла для вас. Если вы считаете, что скорость вставки медленная, я буду продолжать играть с настройками, чтобы ее оптимизировать. – playsted

+0

FYI Я добавил код со случайными данными, показывающими похожие скорости вставки. – playsted