د ریښتیني وخت خدمت مثال په کارولو سره د Q او KDB+ ژبې ځانګړتیاوې

تاسو کولی شئ پدې اړه ولولئ چې د KDB + بیس، د Q پروګرام کولو ژبه څه دي، زما په تیرو کې د دوی ځواک او ضعف څه دي مقالې او په لنډه توګه په سریزه کې. په مقاله کې، موږ به په Q کې یو خدمت پلي کړو چې د راتلونکی ډیټا جریان به پروسس کړي او هره دقیقه د "ریښتیني وخت" حالت کې د راټولولو مختلف فعالیتونه محاسبه کړي (د بیلګې په توګه، دا به د معلوماتو د بلې برخې څخه مخکې د هرڅه محاسبه کولو وخت ولري). د Q اصلي ځانګړنه دا ده چې دا د ویکتور ژبه ده چې تاسو ته اجازه درکوي چې د واحد شیانو سره کار ونه کړئ، مګر د دوی د صفونو، د صفونو او نورو پیچلو شیانو سره. ژبې لکه Q او د هغې خپلوان K, J, APL د دوی د لنډوالي لپاره مشهور دي. ډیری وختونه ، یو برنامه چې د جاوا په څیر پیژندل شوې ژبه کې د کوډ څو سکرینونه نیسي په څو کرښو کې لیکل کیدی شي. دا هغه څه دي چې زه غواړم پدې مقاله کې وښیم.

د ریښتیني وخت خدمت مثال په کارولو سره د Q او KDB+ ژبې ځانګړتیاوې

پېژندنه

KDB+ یو کالم ډیټابیس دی چې په خورا لوی مقدار کې ډیټا باندې تمرکز کوي، په ځانګړي ډول ترتیب شوی (په ابتدايي توګه د وخت له مخې). دا په عمده توګه په مالي ادارو کې کارول کیږي - بانکونه، د پانګونې فنډونه، د بیمې شرکتونه. د Q ژبه د KDB+ داخلي ژبه ده چې تاسو ته اجازه درکوي په مؤثره توګه د دې ډاټا سره کار وکړئ. د Q نظریه لنډیز او موثریت دی، پداسې حال کې چې وضاحت قرباني کیږي. دا د دې حقیقت له مخې توجیه کیږي چې د ویکتور ژبه به په هر حالت کې پوهیدل ستونزمن وي، او د ثبت کولو لنډیز او بډایه تاسو ته اجازه درکوي چې په یوه سکرین کې د پروګرام ډیره لویه برخه وګورئ، کوم چې په نهایت کې د پوهیدلو لپاره اسانه کوي.

پدې مقاله کې موږ په Q کې یو بشپړ برنامه پلي کوو او تاسو ممکن دا هڅه وکړئ. د دې کولو لپاره، تاسو به اصلي Q ته اړتیا ولرئ. تاسو کولی شئ د kx شرکت ویب پاڼې څخه وړیا 32-bit نسخه ډاونلوډ کړئ - www.kx.com. هلته، که تاسو علاقه لرئ، تاسو به د Q، کتاب په اړه د حوالې معلومات ومومئ Q د مړو لپاره او په دې موضوع مختلفې مقالې.

د ستونزې تشکیل

یوه سرچینه شتون لري چې په هر 25 ملی ثانیو کې د معلوماتو سره میز لیږي. څرنګه چې KDB+ په اصل کې په مالیه کې کارول کیږي، موږ به فرض کړو چې دا د معاملو (تجارت) جدول دی، چې لاندې کالمونه لري: وخت (وخت په ملی ثانیو کې)، سیم (د سټاک ایکسچینج کې د شرکت نومول - IBM, AAPL,…)، بیه (هغه قیمت چې ونډې په کې اخیستل شوې وې)، اندازه (د راکړې ورکړې اندازه). د 25 ملی ثانیه وقفه په خپله خوښه ده، نه ډیر کوچنی او نه ډیر اوږد. د دې شتون پدې معنی دی چې ډاټا خدمت ته راځي چې دمخه بفر شوي. دا به اسانه وي چې د خدمت اړخ کې بفرینګ پلي کړئ ، پشمول د اوسني بار پورې اړوند متحرک بفرینګ ، مګر د سادګۍ لپاره ، موږ به په یو ثابت وقفه تمرکز وکړو.

خدمت باید د سم کالم څخه د هر راتلونکي سمبول لپاره هره دقیقه د مجموعې دندو یوه سیټ حساب کړي - اعظمي قیمت ، اوسط قیمت ، د اندازې اندازه او داسې نور. ګټور معلومات. د سادګۍ لپاره، موږ به فرض کړو چې ټولې دندې په زیاتیدونکي توګه محاسبه کیدی شي، د بیلګې په توګه. د نوي ارزښت ترلاسه کولو لپاره، دا کافي ده چې دوه شمیرې وپیژنئ - زاړه او راتلونکی ارزښتونه. د مثال په توګه، افعال max، اوسط، مجموعه دا ملکیت لري، مګر منځنۍ فعالیت نه لري.

موږ به دا هم فرض کړو چې د راتلونکي ډیټا جریان د وخت امر شوی. دا به موږ ته فرصت راکړي چې یوازې د وروستي دقیقې سره کار وکړو. په عمل کې، دا کافي ده چې د اوسني او تیرو دقیقو سره کار کولو توان ولرئ که چیرې ځینې تازه معلومات ناوخته وي. د سادگي لپاره، موږ به دا قضیه په پام کې ونیسو.

د راټولولو دندې

د راټولولو اړین دندې لاندې لیست شوي دي. ما د امکان تر حده ډیری یې اخیستي ترڅو په خدمت کې بار ډیر کړي:

  • لوړ - اعظمي قیمت - په دقیقه کې اعظمي قیمت.
  • ټیټ - دقیق قیمت - په دقیقه کې لږترلږه قیمت.
  • لومړی قیمت - لومړی قیمت - په یوه دقیقه کې لومړی قیمت.
  • وروستی قیمت - وروستی قیمت - په دقیقه کې وروستی قیمت.
  • لومړی اندازه - لومړی اندازه - په یوه دقیقه کې د سوداګرۍ لومړی اندازه.
  • وروستی اندازه - وروستی اندازه - په یوه دقیقه کې د سوداګرۍ وروستی اندازه.
  • numTrades – شمیرنه i – په هره دقیقه کې د تجارتونو شمیر.
  • حجم - اندازه اندازه - په یوه دقیقه کې د سوداګرۍ اندازې مجموعه.
  • pvolume - د قیمت مجموعه - په یوه دقیقه کې د قیمتونو مجموعه، د اوسط قیمت لپاره اړین دي.
  • - د تبادلې نرخ * اندازه - په هره دقیقه کې د معاملو ټول حجم.
  • avgPrice – pvolume%numTrades – اوسط قیمت په یوه دقیقه کې.
  • avgSize - حجم٪ numTrades - په هره دقیقه کې د منځنۍ سوداګرۍ اندازه.
  • vwap - د تبادلې٪ حجم - په هره دقیقه کې اوسط قیمت د راکړې ورکړې اندازې سره وزن لري.
  • cumVolume - د مجموعې حجم - په ټول وخت کې د راکړې ورکړې اندازه.

راځئ سمدلاسه په یوه غیر څرګند ټکي بحث وکړو - دا کالمونه د لومړي ځل لپاره او د هرې بلې دقیقې لپاره څنګه پیل کړئ. د لومړي قیمت ډول ځینې کالمونه باید هر ځل د ړنګولو لپاره پیل شي؛ د دوی ارزښت نامعلوم دی. د حجم نور ډولونه باید تل 0 ته ټاکل شي. دلته داسې کالمونه هم شتون لري چې ګډې طریقې ته اړتیا لري - د بیلګې په توګه، کم حجم باید د تیرې دقیقې څخه کاپي شي، او د لومړي لپاره 0 ته ټاکل شوي وي. راځئ چې دا ټول پیرامیټونه د لغت ډیټا په کارولو سره تنظیم کړو. ډول (د ریکارډ سره ورته والی):

// list ! list – создать словарь, 0n – float null, 0N – long null, `sym – тип символ, `sym1`sym2 – список символов
initWith:`sym`time`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover`avgPrice`avgSize`vwap`cumVolume!(`;00:00;0n;0n;0n;0n;0N;0N;0;0;0.0;0.0;0n;0n;0n;0);
aggCols:reverse key[initWith] except `sym`time; // список всех вычисляемых колонок, reverse объяснен ниже

ما د اسانتیا لپاره په لغت کې سیم او وخت اضافه کړ، اوس initWith د وروستي راټول شوي جدول څخه چمتو شوې کرښه ده، چیرته چې دا د سم سم او وخت ټاکلو لپاره پاتې دي. تاسو کولی شئ دا په میز کې د نوي قطارونو اضافه کولو لپاره وکاروئ.

موږ به aggCols ته اړتیا ولرو کله چې د راټولولو فنکشن رامینځته کړو. لیست باید د هغه ترتیب له امله بدل شي چې په Q کې څرګندونې ارزول کیږي (له ښیې څخه کیڼ ته). هدف دا دی چې ډاډ ترلاسه شي چې محاسبه له لوړ څخه کم حجم ته ځي، ځکه چې ځینې کالمونه په پخوانیو پورې اړه لري.

هغه کالمونه چې اړتیا لري له مخکینۍ څخه نوې دقیقې ته کاپي شي، سم کالم د اسانتیا لپاره اضافه شوی:

rollColumns:`sym`cumVolume;

اوس راځئ چې کالمونه په ګروپونو ویشو چې څنګه باید نوي شي. درې ډولونه توپیر کیدی شي:

  1. جمع کوونکي (حجم، بدلون، ..) - موږ باید راتلونکی ارزښت تیر ته اضافه کړو.
  2. د ځانګړي ټکي سره (لوړ، ټیټ، ..) - په دقیقه کې لومړی ارزښت د راتلونکو معلوماتو څخه اخیستل کیږي، پاتې نور د فنکشن په کارولو سره محاسبه کیږي.
  3. استراحت تل د فنکشن په کارولو سره محاسبه کیږي.

راځئ چې د دې ټولګیو لپاره متغیرات تعریف کړو:

accumulatorCols:`numTrades`volume`pvolume`turnover;
specialCols:`high`low`firstPrice`firstSize;

د محاسبې ترتیب

موږ به په دوه مرحلو کې راټول شوی جدول تازه کړو. د موثریت لپاره، موږ لومړی راتلونکی میز لنډ کړو ترڅو د هر کرکټر او دقیقې لپاره یوازې یو قطار وي. دا حقیقت چې زموږ ټولې دندې مخ په زیاتیدو دي او د دې تضمین کوي ​​​​چې د دې اضافي ګام پایله به بدلون ونلري. تاسو کولی شئ د انتخاب په کارولو سره میز لنډ کړئ:

select high:max price, low:min price … by sym,time.minute from table

دا طریقه یو زیان لري - د حساب شوي کالمونو سیټ مخکې تعریف شوی. خوشبختانه، په Q کې، انتخاب هم د یو فنکشن په توګه پلي کیږي چیرې چې تاسو کولی شئ په متحرک ډول جوړ شوي دلیلونه ځای په ځای کړئ:

?[table;whereClause;byClause;selectClause]

زه به د استدلالونو بڼه په تفصیل سره بیان نه کړم؛ زموږ په قضیه کې، یوازې او غوره څرګندونې به غیر معمولي وي او دوی باید د فورمو کالمونو لغتونه وي! په دې توګه، د کمښت فعالیت په لاندې ډول تعریف کیدی شي:

selExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover!parse each ("max price";"min price";"first price";"last price";"first size";"last size";"count i";"sum size";"sum price";"sum price*size"); // each это функция map в Q для одного списка
preprocess:?[;();`sym`time!`sym`time.minute;selExpression];

د وضاحت لپاره ، ما د پارس فنکشن کارولی ، کوم چې د Q بیان سره تار په داسې ارزښت بدلوي چې د eval فنکشن ته لیږدول کیدی شي او کوم چې د فنکشن انتخاب کې اړین وي. همدارنګه په یاد ولرئ چې پری پروسس د انتخاب فنکشن د پروجیکشن (د بیلګې په توګه د جزوی تعریف شوي دلیلونو سره فنکشن) په توګه تعریف شوی ، یو دلیل (جدول) ورک دی. که موږ په میز کې پری پروسس پلي کړو، نو موږ به یو کمپریس شوی میز ترلاسه کړو.

دویمه مرحله د راټول شوي جدول تازه کول دي. راځئ لومړی په سیوډوکوډ کې الګوریتم ولیکو:

for each sym in inputTable
  idx: row index in agg table for sym+currentTime;
  aggTable[idx;`high]: aggTable[idx;`high] | inputTable[sym;`high];
  aggTable[idx;`volume]: aggTable[idx;`volume] + inputTable[sym;`volume];
  …

په Q کې، د لوپونو پر ځای د نقشې/کمولو افعال کارول معمول دي. مګر څرنګه چې Q د ویکتور ژبه ده او موږ کولی شو په اسانۍ سره په ټولو سمبولونو کې ټول عملیات په یو وخت کې پلي کړو، نو په لومړي سر کې موږ کولی شو پرته له لوپ پرته ترسره کړو، په ټولو سمبولونو کې په یوځل کې عملیات ترسره کول:

idx:calcIdx inputTable;
row:aggTable idx;
aggTable[idx;`high]: row[`high] | inputTable`high;
aggTable[idx;`volume]: row[`volume] + inputTable`volume;
…

مګر موږ کولی شو نور هم لاړ شو، Q یو ځانګړی او خورا پیاوړی آپریټر لري - د عمومي دندې آپریټر. دا تاسو ته اجازه درکوي د شاخصونو، دندو او دلیلونو لیست په کارولو سره په پیچلي ډیټا جوړښت کې د ارزښتونو سیټ بدل کړئ. زموږ په قضیه کې دا داسې ښکاري:

idx:calcIdx inputTable;
rows:aggTable idx;
// .[target;(idx0;idx1;..);function;argument] ~ target[idx 0;idx 1;…]: function[target[idx 0;idx 1;…];argument], в нашем случае функция – это присваивание
.[aggTable;(idx;aggCols);:;flip (row[`high] | inputTable`high;row[`volume] + inputTable`volume;…)];

له بده مرغه، میز ته د ټاکلو لپاره تاسو د قطارونو لیست ته اړتیا لرئ، نه د کالمونو، او تاسو باید د فلیپ فنکشن په کارولو سره میټریکس (د قطارونو لیست ته د کالمونو لیست) انتقال کړئ. دا د لوی میز لپاره ګران دی، نو د دې پرځای موږ د نقشې فنکشن په کارولو سره په هر کالم کې په جلا توګه عمومي دنده پلي کوو (کوم چې د اپوسټروف په څیر ښکاري):

.[aggTable;;:;]'[(idx;)each aggCols; (row[`high] | inputTable`high;row[`volume] + inputTable`volume;…)];

موږ بیا د فنکشن پروجیکشن کاروو. همدارنګه په یاد ولرئ چې په Q کې، د لیست جوړول هم یو فنکشن دی او موږ کولی شو د هر (نقشې) فنکشن په کارولو سره د لیستونو لیست ترلاسه کړو.

د دې لپاره چې ډاډ ترلاسه شي چې د حساب شوي کالمونو سیټ ثابت نه دی، موږ به پورته بیان په متحرک ډول جوړ کړو. راځئ چې لومړی د هر کالم محاسبه کولو لپاره افعال تعریف کړو، د قطار او inp متغیرونو په کارولو سره راټول شوي او ان پټ ډاټا ته مراجعه وکړو:

aggExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`avgPrice`avgSize`vwap`cumVolume!
 ("row[`high]|inp`high";"row[`low]&inp`low";"row`firstPrice";"inp`lastPrice";"row`firstSize";"inp`lastSize";"pvolume%numTrades";"volume%numTrades";"turnover%volume";"row[`cumVolume]+inp`volume");

ځینې ​​کالمونه ځانګړي دي؛ د دوی لومړی ارزښت باید د فنکشن لخوا محاسبه نشي. موږ کولی شو معلومه کړو چې دا د قطار [`numTrades] کالم لخوا لومړی دی - که دا 0 ولري، نو ارزښت لومړی دی. Q د انتخاب فعالیت لري - ?

// high -> ?[isFirst;inp`high;row[`high]|inp`high]
// @ - тоже обобщенное присваивание для случая когда индекс неглубокий
@[`aggExpression;specialCols;{[x;y]"?[isFirst;inp`",y,";",x,"]"};string specialCols];

دلته ما د خپل فعالیت سره یو عمومي شوی دنده وبلله (په منحل منحلاتو کې بیان). دا اوسنی ارزښت (لومړی دلیل) او یو اضافي دلیل ترلاسه کوي، کوم چې زه په څلورم پیرامیټر کې تیروم.

راځئ چې د بیټرۍ سپیکرې په جلا توګه اضافه کړو، ځکه چې فعالیت د دوی لپاره ورته دی:

// volume -> row[`volume]+inp`volume
aggExpression[accumulatorCols]:{"row[`",x,"]+inp`",x } each string accumulatorCols;

دا د Q معیارونو لخوا عادي دنده ده، مګر زه په یوځل کې د ارزښتونو لیست وړاندې کوم. په نهایت کې ، راځئ چې اصلي فعالیت رامینځته کړو:

// ":",/:aggExprs ~ map[{":",x};aggExpr] => ":row[`high]|inp`high" присвоим вычисленное значение переменной, потому что некоторые колонки зависят от уже вычисленных значений
// string[cols],'exprs ~ map[,;string[cols];exprs] => "high:row[`high]|inp`high" завершим создание присваивания. ,’ расшифровывается как map[concat]
// ";" sv exprs – String from Vector (sv), соединяет список строк вставляя “;” посредине
updateAgg:value "{[aggTable;idx;inp] row:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols;(",(";"sv string[aggCols],'":",/:aggExpression aggCols),")]}";

د دې بیان سره، زه په متحرک ډول د تار څخه یو فنکشن جوړوم چې هغه بیان لري چې ما پورته کړی. پایله به داسې ښکاري:

{[aggTable;idx;inp] rows:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols ;(cumVolume:row[`cumVolume]+inp`cumVolume;… ; high:?[isFirst;inp`high;row[`high]|inp`high])]}

د کالم ارزونې ترتیب بدل شوی دی ځکه چې په Q کې د ارزونې ترتیب له ښیې څخه کیڼ ته دی.

اوس موږ د محاسبې لپاره دوه اصلي دندې لرو، موږ یوازې یو څه زیربنا اضافه کولو ته اړتیا لرو او خدمت چمتو دی.

وروستي ګامونه

موږ پری پروسس او تازه اګ افعال لرو چې ټول کار کوي. مګر دا لاهم اړینه ده چې د دقیقو له لارې سم لیږد ډاډمن کړئ او د راټولولو لپاره شاخصونه محاسبه کړئ. تر ټولو لومړی، راځئ چې د init فنکشن تعریف کړو:

init:{
  tradeAgg:: 0#enlist[initWith]; // создаем пустую типизированную таблицу, enlist превращает словарь в таблицу, а 0# означает взять 0 элементов из нее
  currTime::00:00; // начнем с 0, :: означает, что присваивание в глобальную переменную
  currSyms::`u#`symbol$(); // `u# - превращает список в дерево, для ускорения поиска элементов
  offset::0; // индекс в tradeAgg, где начинается текущая минута 
  rollCache:: `sym xkey update `u#sym from rollColumns#tradeAgg; // кэш для последних значений roll колонок, таблица с ключом sym
 }

موږ به د رول فنکشن هم تعریف کړو، کوم چې اوسنۍ دقیقه به بدل کړي:

roll:{[tm]
  if[currTime>tm; :init[]]; // если перевалили за полночь, то просто вызовем init
  rollCache,::offset _ rollColumns#tradeAgg; // обновим кэш – взять roll колонки из aggTable, обрезать, вставить в rollCache
  offset::count tradeAgg;
  currSyms::`u#`$();
 }

موږ به د نوي کرکټرونو اضافه کولو لپاره فنکشن ته اړتیا ولرو:

addSyms:{[syms]
  currSyms,::syms; // добавим в список известных
  // добавим в таблицу sym, time и rollColumns воспользовавшись обобщенным присваиванием.
  // Функция ^ подставляет значения по умолчанию для roll колонок, если символа нет в кэше. value flip table возвращает список колонок в таблице.
  `tradeAgg upsert @[count[syms]#enlist initWith;`sym`time,cols rc;:;(syms;currTime), (initWith cols rc)^value flip rc:rollCache ([] sym: syms)];
 }

او په نهایت کې ، upd فنکشن (د Q خدماتو لپاره د دې فنکشن دودیز نوم) ، کوم چې د پیرودونکي لخوا د معلوماتو اضافه کولو لپاره ویل کیږي:

upd:{[tblName;data] // tblName нам не нужно, но обычно сервис обрабатывает несколько таблиц 
  tm:exec distinct time from data:() xkey preprocess data; // preprocess & calc time
  updMinute[data] each tm; // добавим данные для каждой минуты
};
updMinute:{[data;tm]
  if[tm<>currTime; roll tm; currTime::tm]; // поменяем минуту, если необходимо
  data:select from data where time=tm; // фильтрация
  if[count msyms:syms where not (syms:data`sym)in currSyms; addSyms msyms]; // новые символы
  updateAgg[`tradeAgg;offset+currSyms?syms;data]; // обновим агрегированную таблицу. Функция ? ищет индекс элементов списка справа в списке слева.
 };

بس نور څه نه. دلته زموږ د خدمت بشپړ کوډ دی، لکه څنګه چې ژمنه شوې، یوازې یو څو کرښې:

initWith:`sym`time`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover`avgPrice`avgSize`vwap`cumVolume!(`;00:00;0n;0n;0n;0n;0N;0N;0;0;0.0;0.0;0n;0n;0n;0);
aggCols:reverse key[initWith] except `sym`time;
rollColumns:`sym`cumVolume;

accumulatorCols:`numTrades`volume`pvolume`turnover;
specialCols:`high`low`firstPrice`firstSize;

selExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover!parse each ("max price";"min price";"first price";"last price";"first size";"last size";"count i";"sum size";"sum price";"sum price*size");
preprocess:?[;();`sym`time!`sym`time.minute;selExpression];

aggExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`avgPrice`avgSize`vwap`cumVolume!("row[`high]|inp`high";"row[`low]&inp`low";"row`firstPrice";"inp`lastPrice";"row`firstSize";"inp`lastSize";"pvolume%numTrades";"volume%numTrades";"turnover%volume";"row[`cumVolume]+inp`volume");
@[`aggExpression;specialCols;{"?[isFirst;inp`",y,";",x,"]"};string specialCols];
aggExpression[accumulatorCols]:{"row[`",x,"]+inp`",x } each string accumulatorCols;
updateAgg:value "{[aggTable;idx;inp] row:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols;(",(";"sv string[aggCols],'":",/:aggExpression aggCols),")]}"; / '

init:{
  tradeAgg::0#enlist[initWith];
  currTime::00:00;
  currSyms::`u#`symbol$();
  offset::0;
  rollCache:: `sym xkey update `u#sym from rollColumns#tradeAgg;
 };
roll:{[tm]
  if[currTime>tm; :init[]];
  rollCache,::offset _ rollColumns#tradeAgg;
  offset::count tradeAgg;
  currSyms::`u#`$();
 };
addSyms:{[syms]
  currSyms,::syms;
  `tradeAgg upsert @[count[syms]#enlist initWith;`sym`time,cols rc;:;(syms;currTime),(initWith cols rc)^value flip rc:rollCache ([] sym: syms)];
 };

upd:{[tblName;data] updMinute[data] each exec distinct time from data:() xkey preprocess data};
updMinute:{[data;tm]
  if[tm<>currTime; roll tm; currTime::tm];
  data:select from data where time=tm;
  if[count msyms:syms where not (syms:data`sym)in currSyms; addSyms msyms];
  updateAgg[`tradeAgg;offset+currSyms?syms;data];
 };

ازمايښت

راځئ چې د خدماتو فعالیت وګورو. د دې کولو لپاره، راځئ چې دا په جلا پروسې کې پرمخ بوځو (کوډ د service.q فایل کې واچوئ) او د init فنکشن ته زنګ ووهئ:

q service.q –p 5566

q)init[]

په بل کنسول کې، د دویم Q پروسه پیل کړئ او لومړی سره وصل کړئ:

h:hopen `:host:5566
h:hopen 5566 // если оба на одном хосте

لومړی، راځئ چې د سمبولونو لیست جوړ کړو - 10000 ټوټې او د تصادفي میز جوړولو لپاره فنکشن اضافه کړئ. په دوهم کنسول کې:

syms:`IBM`AAPL`GOOG,-9997?`8
rnd:{[n;t] ([] sym:n?syms; time:t+asc n#til 25; price:n?10f; size:n?10)}

ما په لیست کې درې اصلي سمبولونه اضافه کړل ترڅو په میز کې د دوی په لټه کې اسانه کړي. د rnd فنکشن د n قطارونو سره یو تصادفي جدول رامینځته کوي ، چیرې چې وخت له t څخه تر t + 25 ملی ثانیو پورې توپیر لري.

اوس تاسو کولی شئ خدمت ته د معلوماتو لیږلو هڅه وکړئ (لومړي لس ساعته اضافه کړئ):

{h (`upd;`trade;rnd[10000;x])} each `time$00:00 + til 60*10

تاسو کولی شئ په خدمت کې وګورئ چې میز تازه شوی دی:

c 25 200
select from tradeAgg where sym=`AAPL
-20#select from tradeAgg where sym=`AAPL

پایلې:

sym|time|high|low|firstPrice|lastPrice|firstSize|lastSize|numTrades|volume|pvolume|turnover|avgPrice|avgSize|vwap|cumVolume
--|--|--|--|--|--------------------------------
AAPL|09:27|9.258904|9.258904|9.258904|9.258904|8|8|1|8|9.258904|74.07123|9.258904|8|9.258904|2888
AAPL|09:28|9.068162|9.068162|9.068162|9.068162|7|7|1|7|9.068162|63.47713|9.068162|7|9.068162|2895
AAPL|09:31|4.680449|0.2011121|1.620827|0.2011121|1|5|4|14|9.569556|36.84342|2.392389|3.5|2.631673|2909
AAPL|09:33|2.812535|2.812535|2.812535|2.812535|6|6|1|6|2.812535|16.87521|2.812535|6|2.812535|2915
AAPL|09:34|5.099025|5.099025|5.099025|5.099025|4|4|1|4|5.099025|20.3961|5.099025|4|5.099025|2919

راځئ چې اوس د بار ازموینې ترسره کړو ترڅو معلومه کړو چې خدمت په یوه دقیقه کې څومره ډیټا پروسس کولی شي. اجازه راکړئ تاسو ته یادونه وکړم چې موږ د تازه کولو وقفه 25 ملی ثانیو ته ټاکلې. په دې اساس، خدمت باید (په اوسط ډول) په هر تازه کې لږترلږه 20 ملی ثانیو کې فټ شي ترڅو کاروونکو ته د معلوماتو غوښتنه کولو وخت ورکړي. په دویمه پروسه کې لاندې داخل کړئ:

tm:10:00:00.000
stressTest:{[n] 1 string[tm]," "; times,::h ({st:.z.T; upd[`trade;x]; .z.T-st};rnd[n;tm]); tm+:25}
start:{[n] times::(); do[4800;stressTest[n]]; -1 " "; `min`avg`med`max!(min times;avg times;med times;max times)}

4800 دوه دقیقې دي. تاسو کولی شئ په هر 1000 ملی ثانیو کې د 25 قطارونو لپاره لومړی د چلولو هڅه وکړئ:

start 1000

زما په قضیه کې، پایله په هر تازه کې د څو ملی ثانیو شاوخوا ده. نو زه به سمدلاسه د قطارونو شمیر 10.000 ته لوړ کړم:

start 10000

پایلې:

min| 00:00:00.004
avg| 9.191458
med| 9f
max| 00:00:00.030

یوځل بیا ، کوم ځانګړی ندی ، مګر دا په یوه دقیقه کې 24 ملیون لینونه دي ، په ثانیه کې 400 زره. د 25 ملی ثانیو څخه ډیر لپاره، اوسمهال یوازې 5 ځله ورو شوی، ظاهرا کله چې دقیقه بدله شوه. راځئ چې 100.000 ته لوړ کړو:

start 100000

پایلې:

min| 00:00:00.013
avg| 25.11083
med| 24f
max| 00:00:00.108
q)sum times
00:02:00.532

لکه څنګه چې تاسو لیدلی شئ، خدمت کولی شي په سختۍ سره مقابله وکړي، مګر سره له دې چې دا اداره کوي چې پاتې شي. د معلوماتو دا ډول حجم (په یوه دقیقه کې 240 ملیون قطارونه) خورا لوی دی؛ په داسې حاالتو کې ، دا معمول دی چې د خدماتو ډیری کلونونه (یا حتی لسګونه کلونونه) پیل کړي ، چې هر یو یې یوازې د کرکټرونو برخه پروسس کوي. بیا هم، پایله د تشریح شوي ژبې لپاره اغیزمنه ده چې په ابتدايي توګه د معلوماتو ذخیره کولو تمرکز کوي.

پوښتنه راپورته کیدی شي چې ولې وخت د هر تازه اندازې سره په غیر خطي ډول وده کوي. دلیل یې دا دی چې د shrink فنکشن په حقیقت کې د C فنکشن دی، کوم چې د updateAgg په پرتله خورا اغیزمن دی. د یوې ټاکلې تازه اندازې (شاوخوا 10.000) څخه پیل کول ، updateAgg خپل حد ته رسي او بیا یې د اجرا کولو وخت د اوسمهال اندازې پورې اړه نلري. دا د لومړني ګام Q له امله دی چې خدمت د دې وړتیا لري چې دومره مقدار ډیټا هضم کړي. دا په ګوته کوي چې د لوی ډیټا سره کار کولو پرمهال د سم الګوریتم غوره کول څومره مهم دي. بل ټکی په حافظه کې د معلوماتو سمه ذخیره ده. که چیرې معلومات په کالمري ډول نه وي زیرمه شوي یا د وخت لخوا ترتیب شوي ندي ، نو موږ به د داسې شی سره آشنا شو لکه د TLB کیچ مس - د پروسیسر پته کیچ کې د حافظې پا pageې پتې نشتوالی. د یوې پتې لټون د ناکامۍ په صورت کې شاوخوا 30 ځله ډیر وخت نیسي، او که چیرې معلومات خپاره شي، دا کولی شي خدمت څو ځله ورو کړي.

پایلې

پدې مقاله کې ما وښودله چې د KDB+ او Q ډیټابیس نه یوازې د لوی ډیټا ذخیره کولو او د انتخاب له لارې په اسانۍ سره لاسرسي لپاره مناسب دي ، بلکه د ډیټا پروسس کولو خدماتو رامینځته کولو لپاره هم مناسب دي چې حتی د سلګونو ملیون قطارونو / ګیګابایټ ډیټا هضم کولو وړتیا لري. یو واحد Q پروسه د Q ژبه پخپله د ډیټا پروسس کولو پورې اړوند الګوریتمونو خورا لنډ او موثر پلي کولو ته اجازه ورکوي د دې ویکٹر طبیعت له امله ، د SQL ډیلیکټ ژباړونکي او د کتابتون د کارونو خورا بریالۍ سیټ له امله.

زه به یادونه وکړم چې پورته یوازې د هغه څه برخه ده چې Q یې کولی شي، دا نور ځانګړي ځانګړتیاوې هم لري. د مثال په توګه، یو خورا ساده IPC پروتوکول چې د انفرادي Q پروسو تر مینځ حد له مینځه وړي او تاسو ته اجازه درکوي په سلګونو دا پروسې په یوه شبکه کې یوځای کړئ، کوم چې د نړۍ په بیلابیلو برخو کې په لسګونو سرورونو کې موقعیت لري.

سرچینه: www.habr.com

Add a comment