Асинхронная загрузка. Тест

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

Конфигурация:
— Серверная машина: Q6600, 8GB, RAID0 из 4HDD, FB 2.5.4 SuperClassic
— Клиентская машина: Core i7, 16GB, SSD.
— Гигабитная сеть.

Тестировался 64-битный IBProvider.

Сразу озвучу результат — наблюдались устойчивые тридцать процентов ускорения работы.

Тест написан на VBScript и работает с провайдером через ADODB.

Выполняется загрузка ~2млн записей из таблицы (LOG_TABLE) с протоколом действий пользователей. В таблице есть текстовый блоб (MSG_LARGE_TEXT), который мы и будет модифицировать.

  • Множество открывается с параметрами adOpenKeyset, adLockPessimistic.
  • Запрещается отложенная загрузка блоб-данных — deferred_data=0
  • Размер памяти под кэширование определен равным 10MB
  • Указываем, что модификации не надо передавать обратно на сервер — disconnected_rowset=true

Асинхронная загрузка включается/выключается через свойство "asynch_fetch". Нулевое значение выключает асинхронную загрузку, единица — включает.

Исходный код теста:

call proc("AsyncFetch", 1)
call proc("SyncFetch", 0)

 MsgBox "Stop"

 call wscript.quit(0)

'-------------------------------------------------------------------------
sub proc(testSign, _
         rs_prop__async_fetch)
 MsgBox "Press For Start"

 call wscript.echo("------------------- "&testSign)

 dim cn
 set cn=createobject("ADODB.Connection")

 cn.Provider="LCPI.IBProvider.3"

 call cn.Open("location=home2:d:\database\re48120.gdb","gamer","vermut")

 call cn.BeginTrans()

 dim rs
 set rs=createobject("ADODB.Recordset")
 rs.ActiveConnection=cn

 rs.Properties("Memory Usage")=10*1024 '10MB
 rs.Properties("deferred_data").Value=0
 rs.Properties("disconnected_rowset").Value=true
 rs.Properties("asynch_fetch").Value=rs_prop__async_fetch

 ' adOpenKeyset, adLockPessimistic
 call rs.Open("select * from LOG_TABLE where ID<2000000",,1,2)

 dim v,v2

 dim nRecs,nUpdates
 nRecs=0
 nUpdates=0

 dim startTime
 startTime=Now()

 wscript.Echo("Start time: "&startTime)

 while(not rs.eof)
  nRecs=nRecs+1

  v=rs("MSG_LARGE_TEXT").Value

  if(not IsNull(v))then
   nUpdates=nUpdates+1

   call rs.Update("MSG_LARGE_TEXT",ucase(v))
  end if

  call rs.MoveNext()

  if(Not IsNull(v))then
   call rs.MovePrevious()

   v2=rs("MSG_LARGE_TEXT").Value

   if(v2<>ucase(v))then
    call err.raise(-1,,"ERROR!")
   end if

   call rs.MoveNext()
  end if
 wend

 dim stopTime
 stopTime=Now()

 wscript.Echo("Stop time : "&stopTime)
 wscript.Echo("Duration  : "&DateDiff("s",startTime,stopTime)&" sec(s)")
 wscript.Echo("Records   : "&nRecs)
 wscript.Echo("Updates   : "&nUpdates)
end sub

Вывод:

------------------- AsyncFetch
Start time: 15.05.2015 11:50:29
Stop time : 15.05.2015 11:51:47
Duration  : 78 sec(s)
Records   : 1999927
Updates   : 12326
------------------- SyncFetch
Start time: 15.05.2015 11:51:57
Stop time : 15.05.2015 11:53:42
Duration  : 105 sec(s)
Records   : 1999927
Updates   : 12326

На следующей картинке отображена загрузка ресурсов клиентского компьютера в процессе работы теста. Первый «горб» относится к асинхронной загрузка, второй — к синхронной.



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

Модификация колонки MSG_LARGE_TEXT является дополнительным «шумом», провоцирующим запуск фоновых операций уборки мусора в локальном хранилище.

Все операции с локальным хранилищем пропускаются через кэш (мы указывали его максимальный размер равным 10MB), поддерживающий многопоточный доступ.

Список потоков, в процессе работы асинхронной загрузки выглядел так:



Поток с идентификатором 25536 как раз и выполняет асинхронную загрузку. 30200 — это основной поток процесса, который выполняет VB-код. Остальные активные потоки — это служебные потоки, поток фоновой выгрузки данных во временный файл, потоки сборщика мусора.

В случае синхронной загрузки данных, картинка с потоками будет практически идентичной. Отсутствует только фоновая загрузка записей — её осуществляет основной поток процесса:



В данном тесте мы грузили данные в локальное хранилище с поддержкой произвольного доступа. Можно еще (асинхронно) загружать данные в режиме «Forward-Only». В этом случае, если клиент будет оперативно выбирать данные из локального кэша, то можно избежать создание временного файла.

Параметры локального хранилища можно узнать, прочитав значения свойств результирующего множества — IBP_RS_INFO: Result Storage Size и IBP_RS_INFO: Using File Storage.

Как-то так. Вот.

Напоследок замечу, что IBProvider (судя по всему) является единственным OLEDB провайдером в которым реализована полноценная поддержка асинхронной загрузки данных. Да и вообще, в целом — единственный и неповторимый 🙂

Leave a Comment