အချိန်နှင့်တစ်ပြေးညီဝန်ဆောင်မှု၏ဥပမာကိုအသုံးပြု၍ Q နှင့် KDB+ ဘာသာစကား၏အင်္ဂါရပ်များ

KDB+ အခြေခံ၊ Q ပရိုဂရမ်းမင်းဘာသာစကားက ဘာလဲ၊ သူတို့ရဲ့ အားသာချက် အားနည်းချက်တွေက ဘာတွေလဲ ဆိုတာတွေကို ဖတ်နိုင်ပါတယ်။ ဆောင်းပါး နိဒါန်းမှာ အကျဉ်းချုံးပါ။ ဆောင်းပါးတွင်၊ အဝင်ဒေတာစီးကြောင်းကို စီမံဆောင်ရွက်ပြီး “အချိန်နှင့်တစ်ပြေးညီ” မုဒ်တွင် မိနစ်တိုင်း အမျိုးမျိုးသော ပေါင်းစပ်လုပ်ဆောင်ချက်များကို တွက်ချက်မည့် Q ဝန်ဆောင်မှုကို အကောင်အထည်ဖော်မည် (ဆိုလိုသည်မှာ ဒေတာနောက်ထပ်အပိုင်းမတိုင်မီ အရာအားလုံးကို တွက်ချက်ရန် အချိန်ရှိလိမ့်မည်)။ Q ၏ အဓိကအင်္ဂါရပ်မှာ အရာဝတ္ထုတစ်ခုတည်းနှင့်မဟုတ်ဘဲ ၎င်းတို့၏ arrays၊ arrays ၏ arrays နှင့် အခြားရှုပ်ထွေးသော အရာဝတ္ထုများဖြင့် လုပ်ဆောင်နိုင်စေမည့် vector language တစ်ခုဖြစ်သည်။ Q နှင့် ၎င်း၏ဆွေမျိုး K, J, APL ကဲ့သို့သော ဘာသာစကားများသည် ၎င်းတို့၏ အတိုချုံးမှုအတွက် ကျော်ကြားသည်။ မကြာခဏဆိုသလို၊ Java ကဲ့သို့သော ရင်းနှီးသောဘာသာစကားဖြင့် ကုဒ်၏မျက်နှာပြင်အများအပြားကို နေရာယူသည့် ပရိုဂရမ်တစ်ခုကို လိုင်းအနည်းငယ်ဖြင့် ရေးသားနိုင်သည်။ ဒါက ဒီဆောင်းပါးမှာ ကျွန်တော်တင်ပြချင်တာပါ။

အချိန်နှင့်တစ်ပြေးညီဝန်ဆောင်မှု၏ဥပမာကိုအသုံးပြု၍ Q နှင့် KDB+ ဘာသာစကား၏အင်္ဂါရပ်များ

နိဒါန်း

KDB+ သည် အလွန်များပြားသော ဒေတာပမာဏအပေါ် အာရုံစိုက်ထားသည့် ကော်လံဘားဒေတာဘေ့စ်တစ်ခုဖြစ်ပြီး တိကျသောနည်းလမ်းဖြင့် (အဓိကအားဖြင့် အချိန်အားဖြင့်) မှာထားသည်။ ငွေရေးကြေးရေးအဖွဲ့အစည်းများ - ဘဏ်များ၊ ရင်းနှီးမြှုပ်နှံမှုရန်ပုံငွေများ၊ အာမခံကုမ္ပဏီများတွင်အဓိကအားဖြင့်အသုံးပြုသည်။ Q ဘာသာစကားသည် ဤဒေတာနှင့် ထိထိရောက်ရောက် လုပ်ဆောင်နိုင်စေသော KDB+ ၏ အတွင်းဘာသာစကားဖြစ်သည်။ Q အယူဝါဒသည် အတိုချုံးနှင့် ထိရောက်မှုရှိပြီး ရှင်းလင်းပြတ်သားမှုကို စွန့်လွှတ်သည်။ မည်သည့်ကိစ္စတွင်မဆို vector ဘာသာစကားကို နားလည်ရန်ခက်ခဲမည်ဖြစ်ပြီး၊ အသံသွင်းခြင်း၏ အတိုချုပ်မှုနှင့် ကြွယ်ဝမှုသည် သင့်အား စခရင်တစ်ခုတည်းတွင် ပိုမိုနားလည်နိုင်စေသည့် ပရိုဂရမ်၏ ပိုကြီးသောအစိတ်အပိုင်းကို မြင်နိုင်စေသည့်အတွက်ကြောင့် ၎င်းသည် မျှတသည်။

ဤဆောင်းပါးတွင် ကျွန်ုပ်တို့သည် Q တွင် ပြည့်စုံသော ပရိုဂရမ်တစ်ခုကို အကောင်အထည်ဖော်ပြီး သင်စမ်းသုံးကြည့်ချင်ပေမည်။ ၎င်းကိုလုပ်ဆောင်ရန်၊ အမှန်တကယ် Q ကို လိုအပ်ပါသည်။ သင်သည် kx ကုမ္ပဏီဝဘ်ဆိုဒ်တွင် အခမဲ့ 32-bit ဗားရှင်းကို ဒေါင်းလုဒ်လုပ်နိုင်ပါသည်။ www.kx.com. အဲဒီမှာ စိတ်ဝင်စားရင် Q စာအုပ်မှာ ကိုးကားတဲ့ အချက်အလက်ကို တွေ့လိမ့်မယ်။ Q သည် လူသားများအတွက် နှင့် ဤအကြောင်းအရာနှင့်ပတ်သက်သော ဆောင်းပါးအမျိုးမျိုး။

ပြဿနာကိုပုံဖော်ခြင်း

25 မီလီစက္ကန့်တိုင်း ဒေတာပါသော ဇယားတစ်ခုကို ပေးပို့သည့် အရင်းအမြစ်တစ်ခု ရှိပါသည်။ KDB+ ကို ဘဏ္ဍာရေးတွင် အဓိကအသုံးပြုသောကြောင့်၊ ၎င်းသည် အောက်ပါကော်လံများပါရှိသော ငွေပေးငွေယူဇယား (ကုန်သွယ်မှုများ) ဖြစ်သည်- အချိန် (မီလီစက္ကန့်အတွင်း အချိန်)၊ sym (စတော့အိတ်ချိန်းတွင် ကုမ္ပဏီသတ်မှတ်ခြင်း - IBM က, AAPL,…), စျေးနှုန်း (ရှယ်ယာဝယ်ယူခဲ့သည့်စျေးနှုန်း), အရွယ်အစား (အရောင်းအဝယ်၏အရွယ်အစား) ။ 25 မီလီစက္ကန့်ကြားကာလသည် မထင်သလို၊ အလွန်သေးငယ်ပြီး မရှည်လွန်းပါ။ ၎င်း၏ရှိနေခြင်းဆိုသည်မှာ ဒေတာများကြားခံပြီးသော ဝန်ဆောင်မှုထံသို့ ရောက်ရှိလာသည်ဟု ဆိုလိုသည်။ လက်ရှိ load ပေါ်မူတည်၍ dynamic buffering အပါအဝင် service side တွင် buffering ကို အကောင်အထည်ဖော်ရန် လွယ်ကူမည်ဖြစ်သော်လည်း ရိုးရှင်းစေရန်အတွက်၊ fixed interval ကို အာရုံစိုက်ပါမည်။

ဝန်ဆောင်မှုသည် sym ကော်လံမှ ဝင်လာသော သင်္ကေတတစ်ခုစီအတွက် မိနစ်တိုင်းတွင် ပေါင်းစပ်လုပ်ဆောင်မှုအစုတစ်ခု - အများဆုံးစျေးနှုန်း၊ ပျမ်းမျှစျေးနှုန်း၊ ပေါင်းလဒ်အရွယ်အစား စသည်ဖြင့် ရေတွက်ရပါမည်။ အသုံးဝင်သောသတင်းအချက်အလက်။ ရိုးရှင်းစေရန်အတွက်၊ လုပ်ဆောင်ချက်များအားလုံးကို တိုးမြင့်တွက်ချက်နိုင်သည်ဟု ကျွန်ုပ်တို့ယူဆပါမည်။ တန်ဖိုးအသစ်တစ်ခုရရှိရန်၊ အဟောင်းနှင့် အဝင်တန်ဖိုးများကို ဂဏန်းနှစ်လုံးသိရန် လုံလောက်ပါသည်။ ဥပမာအားဖြင့်၊ လုပ်ဆောင်ချက်များသည် အမြင့်ဆုံး၊ ပျမ်းမျှ၊ ပေါင်းလဒ်တွင် ဤပိုင်ဆိုင်မှုရှိသည်၊ သို့သော် အလယ်အလတ်လုပ်ဆောင်ချက်သည် မရှိပါ။

အဝင်ဒေတာစီးကြောင်းသည် အချိန်သတ်မှတ်ထားကြောင်းလည်း ကျွန်ုပ်တို့ယူဆပါမည်။ ၎င်းသည် ကျွန်ုပ်တို့အား နောက်ဆုံးမိနစ်ဖြင့်သာ လုပ်ဆောင်ရန် အခွင့်အရေးပေးပါမည်။ လက်တွေ့တွင်၊ အချို့သော အပ်ဒိတ်များ နောက်ကျပါက လက်ရှိနှင့် ယခင် မိနစ်များနှင့် အလုပ်လုပ်ရန် လုံလောက်ပါသည်။ ရိုးရိုးရှင်းရှင်းပြောရရင်တော့ ဒီကိစ္စကို စဉ်းစားမှာ မဟုတ်ပါဘူး။

ပေါင်းစပ်လုပ်ဆောင်ချက်များ

လိုအပ်သော ပေါင်းစပ်လုပ်ဆောင်ချက်များကို အောက်တွင်ဖော်ပြထားပါသည်။ ဝန်ဆောင်မှုအပေါ် ဝန်ပိုတိုးစေရန် ၎င်းတို့ထဲမှ အများအပြားကို တတ်နိုင်သမျှ ငါယူခဲ့သည်-

  • မြင့်မားသောစျေးနှုန်း - တစ်မိနစ်လျှင်အမြင့်ဆုံးစျေးနှုန်း။
  • အနိမ့်ဆုံး-အနည်းဆုံးစျေးနှုန်း- တစ်မိနစ်လျှင် အနည်းဆုံးစျေးနှုန်း။
  • ပထမစျေးနှုန်း - ပထမစျေးနှုန်း - တစ်မိနစ်လျှင် ပထမစျေးနှုန်း။
  • နောက်ဆုံးစျေးနှုန်း - နောက်ဆုံးစျေးနှုန်း - နောက်ဆုံးစျေးနှုန်း။
  • ပထမအရွယ်အစား - ပထမအရွယ်အစား - တစ်မိနစ်လျှင် ပထမဆုံးကုန်သွယ်မှုအရွယ်အစား။
  • နောက်ဆုံးအရွယ်အစား - နောက်ဆုံးအရွယ်အစား - တစ်မိနစ်အတွင်း နောက်ဆုံးကုန်သွယ်မှုအရွယ်အစား။
  • numTrades – တစ်မိနစ်လျှင် အရောင်းအ၀ယ်အရေအတွက်ကို ရေတွက်ပါ။
  • ပမာဏ - ပေါင်းလဒ်အရွယ်အစား - တစ်မိနစ်လျှင် ကုန်သွယ်မှုပမာဏ ပေါင်းလဒ်။
  • pvolume – sum price – avgPrice အတွက် လိုအပ်သော တစ်မိနစ်လျှင် ဈေးနှုန်းများ ပေါင်းလဒ်။
  • - ပေါင်းလဒ်အလှည့်အပြောင်းစျေးနှုန်း* အရွယ်အစား - တစ်မိနစ်လျှင် အရောင်းအဝယ်စုစုပေါင်းပမာဏ။
  • avgPrice – pvolume%numTrades – တစ်မိနစ်လျှင် ပျမ်းမျှစျေးနှုန်း။
  • avgSize – volume%numTrades – တစ်မိနစ်လျှင် ပျမ်းမျှကုန်သွယ်မှုအရွယ်အစား။
  • vwap – turnover%volume – အရောင်းအ၀ယ်အရွယ်အစားဖြင့် တွက်ဆထားသော တစ်မိနစ်လျှင် ပျမ်းမျှစျေးနှုန်း။
  • cumVolume – sum volume – အချိန်တစ်ခုလုံးတွင် ငွေပေးငွေယူ၏ စုပုံထားသောအရွယ်အစား။

ရှင်းရှင်းလင်းလင်းမဟုတ်သောအချက်တစ်ချက်ကို ချက်ခြင်းဆွေးနွေးကြပါစို့ - ဤကော်လံများကို ပထမအကြိမ်နှင့် နောက်မိနစ်တိုင်းအတွက် မည်သို့စတင်ရမည်နည်း။ ပထမPrice အမျိုးအစား၏ အချို့ကော်လံများကို အကြိမ်တိုင်း null အဖြစ် သတ်မှတ်ရမည်ဖြစ်ပြီး ၎င်းတို့၏တန်ဖိုးကို သတ်မှတ်မထားပါ။ အခြားသော အသံအတိုးအကျယ် အမျိုးအစားများကို 0 သို့ အမြဲသတ်မှတ်ထားရပါမည်။ ပေါင်းစပ်ချဉ်းကပ်မှု လိုအပ်သည့် ကော်လံများလည်း ရှိသည် - ဥပမာ၊ cumVolume ကို ယခင်မိနစ်မှ ကူးယူရမည်ဖြစ်ပြီး ပထမတစ်ခုအတွက် 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 объяснен ниже

အဆင်ပြေစေရန်အတွက် အဘိဓာန်သို့ sym နှင့် အချိန်ကို ထည့်လိုက်သည်၊ ယခု initWith သည် မှန်ကန်သော sym နှင့် အချိန်ကို သတ်မှတ်ရန် ကျန်ရှိနေသော နောက်ဆုံး ပေါင်းစပ်ဇယားမှ အဆင်သင့်လုပ်ထားသော စာကြောင်းဖြစ်ပါသည်။ ဇယားတစ်ခုသို့ အတန်းအသစ်များထည့်ရန် ၎င်းကို သင်အသုံးပြုနိုင်ပါသည်။

ပေါင်းစည်းခြင်းလုပ်ဆောင်ချက်ကို ဖန်တီးသောအခါတွင် ကျွန်ုပ်တို့သည် aggCols လိုအပ်ပါသည်။ Q ရှိ စကားရပ်များကို အကဲဖြတ်သည့် အစီအစဥ် (ညာဘက်မှ ဘယ်ဘက်) ကြောင့် စာရင်းကို ပြောင်းပြန်လှန်ရပါမည်။ အချို့ကော်လံများသည် ယခင်အရာများပေါ်တွင်မူတည်သောကြောင့် တွက်ချက်မှုကို အမြင့်ဆုံးမှ cumVolume သို့သွားကြောင်း သေချာစေရန်ဖြစ်သည်။

ယခင်တစ်မိနစ်မှ မိနစ်အသစ်သို့ ကူးယူရန် လိုအပ်သောကော်လံများကို အဆင်ပြေစေရန်အတွက် sym ကော်လံကို ပေါင်းထည့်သည်-

rollColumns:`sym`cumVolume;

ယခု ကော်လံများကို ၎င်းတို့ မွမ်းမံသင့်သည်နှင့်အညီ အုပ်စုများ ခွဲလိုက်ကြပါစို့။ အမျိုးအစားသုံးမျိုးခွဲခြားနိုင်သည်။

  1. Accumulators (ထုထည်၊ အလှည့်အပြောင်း၊..) - ကျွန်ုပ်တို့သည် ယခင်တစ်ခုသို့ အဝင်တန်ဖိုးကို ထည့်ရပါမည်။
  2. အထူးအမှတ် (မြင့်၊ အနိမ့်၊ ..) - မိနစ်အတွင်း ပထမဆုံးတန်ဖိုးကို အဝင်ဒေတာမှ ယူသည်၊ ကျန်ကို လုပ်ဆောင်ချက်ကို အသုံးပြု၍ တွက်ချက်သည်။
  3. အနားယူပါ။ လုပ်ဆောင်ချက်တစ်ခုကို အသုံးပြု၍ အမြဲတွက်ချက်ပါ။

ဤအတန်းများအတွက် ကိန်းရှင်များကို သတ်မှတ်ကြပါစို့။

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

တွက်နည်း

စုစည်းထားသောဇယားကို အဆင့်နှစ်ဆင့်ဖြင့် အပ်ဒိတ်လုပ်ပါမည်။ ထိရောက်မှုရှိစေရန်၊ ကျွန်ုပ်တို့သည် အဝင်ဇယားကို ဦးစွာချုံ့ခြင်းဖြင့် ဇာတ်ကောင်တစ်ခုစီနှင့် မိနစ်တစ်ခုစီအတွက် အတန်းတစ်တန်းသာရှိသည်။ ကျွန်ုပ်တို့၏လုပ်ငန်းဆောင်တာအားလုံးသည် တိုးမြင့်လာပြီး ဆက်စပ်မှုရှိသည်ဟူသောအချက်က ဤနောက်ထပ်အဆင့်၏ရလဒ်သည် ပြောင်းလဲမည်မဟုတ်ကြောင်း အာမခံပါသည်။ Select ကိုသုံးပြီး ဇယားကို ကျုံ့နိုင်သည်-

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 expression နှင့် string တစ်ခုကို eval လုပ်ဆောင်ချက်သို့ ပေးပို့နိုင်သော တန်ဖိုးအဖြစ် ပြောင်းလဲပေးသည့် parse function ကို အသုံးပြုခဲ့သည်။ ကြိုတင်လုပ်ဆောင်မှုကို ရွေးချယ်ထားသောလုပ်ဆောင်ချက်၏ ပရောဂျက်တစ်ခု (ဆိုလိုသည်မှာ၊ တစ်စိတ်တစ်ပိုင်းသတ်မှတ်ထားသော အငြင်းအခုံများပါသည့် လုပ်ဆောင်ချက်တစ်ခု)၊ အငြင်းအခုံတစ်ခု (ဇယား) ပျောက်ဆုံးနေကြောင်းကိုလည်း သတိပြုပါ။ ကျွန်ုပ်တို့သည် ဇယားတစ်ခုသို့ ကြိုတင်လုပ်ဆောင်မှုကို အသုံးချပါက၊ ကျွန်ုပ်တို့သည် ချုံ့ထားသောဇယားကို ရရှိမည်ဖြစ်သည်။

ဒုတိယအဆင့်မှာ စုစည်းထားသော ဇယားကို အဆင့်မြှင့်တင်ခြင်း ဖြစ်သည်။ ပထမဆုံး pseudocode မှာ algorithm ကိုရေးကြည့်ရအောင်။

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 တွင်၊ loops များအစား မြေပုံ/လျော့ချသည့်လုပ်ဆောင်ချက်များကို သုံးလေ့ရှိသည်။ Q သည် vector language ဖြစ်ပြီး သင်္ကေတများအားလုံးတွင် လုပ်ဆောင်ချက်အားလုံးကို တစ်ပြိုင်နက် အလွယ်တကူ အသုံးပြုနိုင်သောကြောင့်၊ ထို့နောက် ပထမအနီးစပ်ဆုံးတွင် loop လုံးဝမပါဘဲ လုပ်ဆောင်နိုင်ပြီး သင်္ကေတအားလုံးကို တစ်ပြိုင်နက် လုပ်ဆောင်နိုင်သည်-

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

သို့သော် ကျွန်ုပ်တို့ ရှေ့ဆက်သွားနိုင်သည်၊ Q တွင် ထူးခြားပြီး အလွန်အစွမ်းထက်သော အော်ပရေတာတစ်ခုရှိသည် - ယေဘူယျအားဖြင့် assignment operator ဖြစ်သည်။ ၎င်းသည် သင့်အား အညွှန်းကိန်းများ၊ လုပ်ဆောင်ချက်များနှင့် အကြောင်းပြချက်များစာရင်းကို အသုံးပြု၍ ရှုပ်ထွေးသောဒေတာဖွဲ့စည်းပုံတွင် တန်ဖိုးအစုတစ်ခုကို ပြောင်းလဲနိုင်စေပါသည်။ ကျွန်ုပ်တို့၏အခြေအနေတွင်၊

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;…)];

ကံမကောင်းစွာဖြင့်၊ ဇယားတစ်ခုသို့သတ်မှတ်ရန်၊ ကော်လံများမဟုတ်ဘဲ အတန်းများစာရင်းတစ်ခုလိုအပ်ပြီး flip လုပ်ဆောင်ချက်ကို အသုံးပြု၍ matrix (ကော်လံစာရင်းသို့ အတန်းများစာရင်း) ကို ကူးပြောင်းရမည်ဖြစ်သည်။ စားပွဲကြီးတစ်ခုအတွက် ၎င်းသည် စျေးကြီးသည်၊ ထို့ကြောင့် ကျွန်ုပ်တို့သည် မြေပုံလုပ်ဆောင်ချက် (ပုံသဏ္ဍာန်ပုံသဏ္ဌာန်တူသော မြေပုံလုပ်ဆောင်ချက်ကို အသုံးပြု၍ ကော်လံတစ်ခုစီအတွက် ယေဘုယျသတ်မှတ်ထားသော တာဝန်ကို သီးခြားစီအသုံးပြုသည်)။

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

ကျွန်ုပ်တို့သည် function projection ကိုထပ်သုံးသည်။ Q တွင်၊ စာရင်းတစ်ခုဖန်တီးခြင်းသည်လည်း လုပ်ဆောင်ချက်တစ်ခုဖြစ်ပြီး စာရင်းစာရင်းတစ်ခုရရှိရန် တစ်ခုစီ(မြေပုံ) လုပ်ဆောင်ချက်ကို အသုံးပြု၍ ၎င်းကို ကျွန်ုပ်တို့ခေါ်ဆိုနိုင်သည်ကို သတိပြုပါ။

တွက်ချက်ထားသော ကော်လံအစုံကို မပြုပြင်ကြောင်း သေချာစေရန်၊ ကျွန်ုပ်တို့သည် အထက်ဖော်ပြပါ စကားရပ်ကို ဒိုင်းနမစ်ဖြင့် ဖန်တီးပါမည်။ ပေါင်းစပ်ပြီး ထည့်သွင်းဒေတာကို ရည်ညွှန်းရန် အတန်းနှင့် inp variables များကို အသုံးပြုကာ ကော်လံတစ်ခုစီကို တွက်ချက်ရန် လုပ်ဆောင်ချက်များကို ဦးစွာ သတ်မှတ်ကြပါစို့။

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 တွင် ရွေးချယ်သည့် လုပ်ဆောင်ချက်တစ်ခု ပါရှိသည် - ?[Boolean list;list1;list2] - ပထမအငြင်းအခုံရှိ အခြေအနေပေါ်မူတည်၍ စာရင်း 1 သို့မဟုတ် 2 မှ တန်ဖိုးတစ်ခုကို ရွေးချယ်သည်-

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

ဤနေရာတွင် ကျွန်ုပ်သည် ကျွန်ုပ်၏လုပ်ဆောင်မှု (ကောက်ကောက်ကောက်ကောက်များပါသည့်အသုံးအနှုန်း) ဖြင့် ယေဘူယျလုပ်ဆောင်သည့်တာဝန်ကို ခေါ်သည်။ ၎င်းသည် လက်ရှိတန်ဖိုး (ပထမအငြင်းပွားမှု) နှင့် 4th ကန့်သတ်ဘောင်တွင် ကျွန်ုပ်ဖြတ်သန်းသည့် နောက်ထပ်အငြင်းအခုံတစ်ခုကို လက်ခံရရှိသည် ။

၎င်းတို့အတွက် လုပ်ဆောင်ချက်သည် တူညီသောကြောင့် ဘက်ထရီစပီကာများကို သီးခြားစီထည့်ကြပါစို့။

// 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 တွင် အကဲဖြတ်မှုအစီအစဥ်သည် ညာဘက်မှ ဘယ်ဘက်တွင်ရှိသောကြောင့် ကော်လံအကဲဖြတ်မှုအစီအစဥ်ကို ပြောင်းပြန်လှန်ထားသည်။

ယခု ကျွန်ုပ်တို့တွင် တွက်ချက်မှုများအတွက် လိုအပ်သော အဓိကလုပ်ဆောင်ချက် နှစ်ခုရှိသည်၊ ကျွန်ုပ်တို့သည် အခြေခံအဆောက်အအုံအနည်းငယ်ကို ထည့်သွင်းရန် လိုအပ်ပြီး ဝန်ဆောင်မှုသည် အဆင်သင့်ဖြစ်နေပါပြီ။

နောက်ဆုံးအဆင့်

ကျွန်ုပ်တို့တွင် အလုပ်အားလုံးကို လုပ်ဆောင်ပေးသည့် preprocess နှင့် updateAgg လုပ်ဆောင်ချက်များရှိသည်။ သို့သော် မိနစ်များမှတစ်ဆင့် မှန်ကန်သောအကူးအပြောင်းကို သေချာစေရန်နှင့် စုစည်းမှုအတွက် အညွှန်းကိန်းများကို တွက်ချက်ရန် လိုအပ်နေသေးသည်။ ပထမဆုံးအနေနဲ့ init function ကို သတ်မှတ်ကြပါစို့။

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 function ကိုလည်း ကျွန်ုပ်တို့ သတ်မှတ်ပါမည်။

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)];
 }

နောက်ဆုံးအနေနှင့်၊ ဒေတာထည့်ရန် client မှခေါ်သည့် 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];
 };

စမ်းသပ်ခြင်း

ဝန်ဆောင်မှု၏စွမ်းဆောင်ရည်ကိုစစ်ဆေးကြပါစို့။ ဒါကိုလုပ်ဖို့၊ သီးခြားလုပ်ငန်းစဥ်တစ်ခုအနေနဲ့ run ကြပါစို့ (ကုဒ်ကို service.q ဖိုင်ထဲမှာထည့်ပါ) နဲ့ init function ကိုခေါ်ပါ-

q service.q –p 5566

q)init[]

အခြား ကွန်ဆိုးလ်တွင်၊ ဒုတိယ Q လုပ်ငန်းစဉ်ကို စတင်ပြီး ပထမနှင့် ချိတ်ဆက်ပါ-

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

ပထမဦးစွာ၊ သင်္ကေတများစာရင်းကိုဖန်တီးကြပါစို့ - အပိုင်းပိုင်း ၁၀,၀၀၀ နှင့် ကျပန်းဇယားတစ်ခုဖန်တီးရန် လုပ်ဆောင်ချက်တစ်ခုထည့်ကြပါစို့။ ဒုတိယ ကွန်ဆိုးလ်တွင်-

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 လုပ်ဆောင်ချက်သည် အချိန်သည် t မှ t+25 မီလီစက္ကန့်အထိ ကွဲပြားသည့် n အတန်းများဖြင့် ကျပန်းဇယားတစ်ခုကို ဖန်တီးသည်။

ယခု သင်သည် ဝန်ဆောင်မှုသို့ ဒေတာပေးပို့ရန် ကြိုးစားနိုင်ပြီ (ပထမဆယ်နာရီအတွင်း ထည့်ပါ)။

{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 အတွက် ဦးစွာ စမ်းသုံးနိုင်သည်-

start 1000

ကျွန်ုပ်၏ကိစ္စတွင်၊ ရလဒ်သည် အပ်ဒိတ်တစ်ခုလျှင် မီလီစက္ကန့်နှစ်ဆယ်ခန့်ဖြစ်သည်။ ထို့ကြောင့် ကျွန်ုပ်သည် အတန်းအရေအတွက်ကို 10.000 သို့ ချက်ချင်းတိုးပေးပါမည်။

start 10000

ရလဒ်:

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

တဖန်၊ ဘာမှ မထူးခြားသော်လည်း၊ ဤသည်မှာ တစ်မိနစ်လျှင် လိုင်း ၂၄ သန်း၊ တစ်စက္ကန့်လျှင် ၄၀၀၀၀၀ ဖြစ်သည်။ 24 မီလီစက္ကန့်ထက် ပိုကြာလာသောအခါတွင် အပ်ဒိတ်သည် 400 ကြိမ်သာ နှေးကွေးသွားသည်၊ မိနစ်ပိုင်းကို ပြောင်းလဲသွားပုံရသည်။ 25 သို့တိုးကြပါစို့။

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) သည် အလွန်ကြီးမားသည်၊ ထိုသို့သော အခြေအနေမျိုးတွင်၊ ဝန်ဆောင်မှု၏ clone အများအပြား (သို့မဟုတ် ဒါဇင်ပေါင်းများစွာသော clone များပင်) ကို စတင်လေ့ရှိသည်၊ တစ်ခုစီသည် ဇာတ်ကောင်များ၏ တစ်စိတ်တစ်ပိုင်းကိုသာ လုပ်ဆောင်သည်။ သို့တိုင်၊ ဒေတာသိုလှောင်မှုအပေါ် အဓိကအာရုံစိုက်သည့် ဘာသာပြန်ဘာသာစကားအတွက် ရလဒ်မှာ အထင်ကြီးစရာဖြစ်သည်။

အပ်ဒိတ်တစ်ခုစီ၏ အရွယ်အစားနှင့် အချိန်သည် အဘယ်ကြောင့် အညီအညွတ် ကြီးထွားလာသည်နှင့်အမျှ မေးခွန်းထုတ်စရာ ဖြစ်လာနိုင်သည်။ အဘယ်ကြောင့်ဆိုသော် shrink function သည် updateAgg ထက်ပိုမိုထိရောက်သော C function ဖြစ်သည်။ အချို့သောအပ်ဒိတ်အရွယ်အစား (10.000 ဝန်းကျင်) မှစတင်၍ updateAgg သည် ၎င်း၏မျက်နှာကျက်သို့ရောက်ရှိပြီး ၎င်း၏လုပ်ဆောင်ချိန်သည် အပ်ဒိတ်အရွယ်အစားပေါ်တွင်မူတည်ခြင်းမရှိပါ။ ဝန်ဆောင်မှုသည် ထိုကဲ့သို့သော ဒေတာပမာဏများကို ချေဖျက်နိုင်သည့် ပဏာမအဆင့် Q ကြောင့်ဖြစ်သည်။ ၎င်းသည် ဒေတာကြီးကြီးမားမားဖြင့် လုပ်ဆောင်သည့်အခါ မှန်ကန်သော အယ်လဂိုရီသမ်ကို ရွေးချယ်ရန် မည်မျှအရေးကြီးကြောင်း မီးမောင်းထိုးပြသည်။ နောက်တစ်ခုကတော့ memory ထဲမှာ data တွေကို မှန်ကန်စွာ သိမ်းဆည်းခြင်းပါပဲ။ ဒေတာကို ကော်လံအတိုင်း သိမ်းဆည်းမထားပါက သို့မဟုတ် အချိန်အလိုက် မှာယူခြင်းမရှိပါက၊ ကျွန်ုပ်တို့သည် TLB cache miss (ပရိုဆက်ဆာလိပ်စာ ကက်ရှ်) ရှိ မန်မိုရီစာမျက်နှာလိပ်စာမရှိခြင်းကဲ့သို့ အရာနှင့် ရင်းနှီးလာမည်ဖြစ်သည်။ လိပ်စာတစ်ခုရှာဖွေခြင်းသည် မအောင်မြင်ပါက အဆ 30 ခန့် ပိုကြာပြီး ဒေတာများ ပြန့်ကျဲနေပါက ဝန်ဆောင်မှုကို အကြိမ်များစွာ နှောင့်နှေးစေနိုင်သည်။

ကောက်ချက်

ဤဆောင်းပါးတွင်၊ KDB+ နှင့် Q ဒေတာဘေ့စ်သည် ကြီးမားသောဒေတာကို သိမ်းဆည်းရန်နှင့် ရွေးချယ်မှုမှတစ်ဆင့် အလွယ်တကူ ဝင်ရောက်ကြည့်ရှုနိုင်ရုံသာမက ဒေတာ အတန်း/ဂစ်ဂါဘိုက် သန်းရာနှင့်ချီသော ဒေတာများကို တန်းစီ/ဂစ်ဂါဘိုက်များကို ချေဖျက်နိုင်သည့် ဒေတာစီမံဆောင်ရွက်ရေးဝန်ဆောင်မှုများကို ဖန်တီးရန်အတွက်လည်း သင့်လျော်ကြောင်း ဤဆောင်းပါးတွင် ကျွန်ုပ်ပြသခဲ့သည်။ Q လုပ်ငန်းစဉ်တစ်ခုတည်း။ Q ဘာသာစကားကိုယ်တိုင်က ၎င်း၏ vector သဘောသဘာဝ၊ built-in SQL ဒေသိယစကားပြန်နှင့် အလွန်အောင်မြင်သော စာကြည့်တိုက်လုပ်ဆောင်ချက်အစုံကြောင့် ဒေတာလုပ်ဆောင်ခြင်းဆိုင်ရာ အယ်လဂိုရီသမ်များကို အလွန်တိကျပြီး ထိရောက်စွာ အကောင်အထည်ဖော်နိုင်စေပါသည်။

အထက်ပါအချက်သည် Q လုပ်ဆောင်နိုင်သည့် အစိတ်အပိုင်းတစ်ခုမျှသာဖြစ်ပြီး ၎င်းတွင် အခြားထူးခြားသောအင်္ဂါရပ်များပါရှိသည်။ ဥပမာအားဖြင့်၊ တစ်ဦးချင်း Q လုပ်ငန်းစဉ်များကြား နယ်နိမိတ်ကို ဖျက်ပေးသည့် အလွန်ရိုးရှင်းသော IPC ပရိုတိုကောသည် သင့်အား ဤလုပ်ငန်းစဉ်ရာပေါင်းများစွာကို ကွန်ရက်တစ်ခုတည်းသို့ ပေါင်းစပ်နိုင်စေကာ ကမ္ဘာ၏ အစိတ်အပိုင်းများစွာရှိ ဆာဗာများစွာတွင် တည်ရှိနိုင်သည်။

source: www.habr.com

မှတ်ချက် Add