Ответ 1
Q: Есть ли какое-нибудь решение, которое может использовать преимущество как структурированный
dtype
вnumpy
?
Работа с данными L2-DoM сопряжена с двумя сложностями по сравнению с данными о ценовой подаче ToB (Top-of-the-Book). а) собственный фид быстрый (очень быстрый/протокол FIX или другие частные фиды данных доставляют записи с сотнями, тысячами (больше во время фундаментальных событий на основных) L2-DoM изменяется в миллисекунду. Обработка и хранение должны быть ориентированы на производительность b ) любого рода в автономном режиме анализа была успешно манипулировать и эффективно обрабатывать большие наборы данных, в связи с характером пункта а)
- Настройки хранения
- Использование
numpy
-alike синтаксических предпочтения - Настройки производительности
Настройки хранения: решено
Учитывая, что pandas.DataFrame
был установлен в качестве предпочтительного типа хранилища, pandas.DataFrame
это, хотя синтаксис и предпочтения производительности могут иметь неблагоприятные последствия.
Можно пойти другим путем, но может привести к неизвестным затратам на рефакторинг/реинжиниринг, которые операционная среда O/P не должна или уже не желает нести.
Сказав это, ограничения возможностей pandas
должны быть учтены при проектировании, и все остальные шаги должны будут соответствовать этому, если это предпочтение не будет пересмотрено в будущем.
Синтаксис numpy
-alike: решен
Этот запрос ясен и понятен, так как numpy
и быстрые инструменты созданы для высокопроизводительного анализа чисел. Учитывая предпочтения набора для хранения, мы будем реализовывать пару numpy
-tricks так, чтобы вписаться в pandas
2D- DataFrame
все при разумных затратах на оба .STORE
и .RETRIEVE
направлений:
# on .STORE:
testDF['ask_DoM'][aRowIDX] = ask200.dumps() # type(ask200) <class 'numpy.ndarray'>
# on .RETRIEVE:
L2_ASK = np.loads( testDF['ask_DoM'][aRowIDX] ) # type(L2_ASK) <class 'numpy.ndarray'>
Настройки производительности: ТЕСТИРОВАНИЕ
Чистые дополнительные расходы на предлагаемое решение для обоих направлений .STORE
и .RETRIEVE
были протестированы на:
.STORE
стоимость .STORE
направления .STORE
составляет не менее 70 [us]
и не более ~ 160 [us]
на ячейку для заданных масштабов массивов L2_DoM (среднее значение: 78 [ms]
StDev: 9-11 [ms]
):
>>> [ f( [testDUMPs() for _ in range(1000)] ) for f in (np.min,np.mean,np.std,np.max) ]
[72, 79.284, 11.004153942943548, 150]
[72, 78.048, 10.546135548152224, 160]
[71, 78.584, 9.887971227708949, 139]
[72, 76.9, 8.827332496286745, 132]
Повторяющаяся стоимость в направлении .RETRIEVE
составляет не менее 46 [us]
и не более ~ 123 [us]
на ячейку для заданных масштабов массивов L2_DoM (avg: 50 [us]
StDev: 9.5 [us]
):
>>> [ f( [testLOADs() for _ in range(1000)] ) for f in (np.min,np.mean,np.std,np.max) ]
[46, 50.337, 9.655194197943405, 104]
[46, 49.649, 9.462272665697178, 123]
[46, 49.513, 9.504293766503643, 123]
[46, 49.77, 8.367165350344164, 114]
[46, 51.355, 6.162434583831296, 89]
Можно ожидать еще более высокой производительности, если использовать более int64
типы данных int64
выровненные по int64
(да, за счет удвоенных затрат на хранение, но при этом затраты на вычисления будут определять, будет ли это движение иметь преимущество в производительности) и благодаря возможности использовать memoryview
[ CN03] манипуляции, которые могут перерезать горло и сократить время задержки до 22 [us]
.
Тест проводился под py3.5.6, numpy v1.15.2, используя:
>>> import numpy as np; ask200 = np.arange( 200, dtype = np.int32 ); s = ask200.dumps()
>>> from zmq import Stopwatch; aClk = Stopwatch()
>>> def testDUMPs():
... aClk.start()
... s = ask200.dumps()
... return aClk.stop()
...
>>> def testLOADs():
... aClk.start()
... a = np.loads( s )
... return aClk.stop()
...
Процессор платформы, иерархия кэша и детали оперативной памяти:
>>> get_numexpr_cpuinfo_details_on_CPU()
'TLB size'______________________________:'1536 4K pages'
'address sizes'_________________________:'48 bits physical, 48 bits virtual'
'apicid'________________________________:'17'
'bogomips'______________________________:'7199.92'
'bugs'__________________________________:'fxsave_leak sysret_ss_attrs null_seg spectre_v1 spectre_v2'
'cache size'____________________________:'2048 KB'
'cache_alignment'_______________________:'64'
'clflush size'__________________________:'64'
'core id'_______________________________:'1'
'cpu MHz'_______________________________:'1400.000'
'cpu cores'_____________________________:'2'
'cpu family'____________________________:'21'
'cpuid level'___________________________:'13'
'flags'_________________________________:'fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc extd_apicid aperfmperf eagerfpu pni pclmulqdq monitor ssse3 cx16 sse4_1 sse4_2 popcnt aes xsave avx lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs xop skinit wdt lwp fma4 nodeid_msr topoext perfctr_core perfctr_nb cpb hw_pstate vmmcall arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold'
'fpu'___________________________________:'yes'
'fpu_exception'_________________________:'yes'
'initial apicid'________________________:'1'
'microcode'_____________________________:'0x6000626'
'model'_________________________________:'1'
'model name'____________________________:'AMD FX(tm)-4100 Quad-Core Processor'
'physical id'___________________________:'0'
'power management'______________________:'ts ttp tm 100mhzsteps hwpstate cpb'
'processor'_____________________________:'1'
'siblings'______________________________:'4'
'stepping'______________________________:'2'
'vendor_id'_____________________________:'AuthenticAMD'
'wp'____________________________________:'yes'