ഒരു നേറ്റീവ് Polling API നിർവചിക്കുന്ന PHP RFC അംഗീകരിക്കപ്പെടുകയും ഭാഷയുടെ മാസ്റ്റർ ബ്രാഞ്ചിലേക്ക് (master branch) ലയിപ്പിക്കുകയും ചെയ്തു. ഇത് ഫയൽ ഡിസ്ക്രിപ്റ്ററുകൾ (file descriptors) പോൾ ചെയ്യാൻ ഡെവലപ്പർമാർക്ക് വേഗതയേറിയതും OS-തലത്തിലുള്ളതുമായ ഒരു മാർഗ്ഗം നൽകുന്നു. അടുത്തിടെ കൂട്ടിച്ചേർത്ത Fibers-നോടൊപ്പം ചേർന്ന്, PHP ഇപ്പോൾ അസിൻക്രണസ് കോഡിനായി (asynchronous code) ഒരു ഇൻ-ബിൽറ്റ്, കാര്യക്ഷമമായ അടിത്തറ വാഗ്ദാനം ചെയ്യുന്നു—ഇതുവരെ ലൈബ്രറികൾ സ്വന്തമായി പരിഹാരങ്ങൾ (work-arounds) കണ്ടെത്തേണ്ടി വന്ന ഒരു വലിയ വിടവ് ഇത് നികത്തുന്നു.

Fibers-ഉം ഇവന്റ് ലൂപ്പും (event loop), വേർതിരിക്കപ്പെടുന്നു

Fibers എന്നത് ഒരു ലോ-ലെവൽ കൺട്രോൾ-ഫ്ലോ പ്രിമിറ്റീവ് (control-flow primitive) ആണ്. ഒരു ഫംഗ്ഷന് അതിന്റെ പ്രവർത്തനം ഒരു പ്രത്യേക ഘട്ടത്തിൽ നിർത്തിവെക്കാനും (suspend), പിന്നീട് അത് എവിടെയാണോ നിർത്തിയത് അവിടെ നിന്ന് തന്നെ പുനരാരംഭിക്കാനും (resume) ഇവ അനുവദിക്കുന്നു. പ്രധാനമായും, ഒരു ഫൈബറിന് സോക്കറ്റുകളെക്കുറിച്ചോ (sockets), ടൈമറുകളെക്കുറിച്ചോ (timers) അല്ലെങ്കിൽ മറ്റ് I/O സ്രോതസ്സുകളെക്കുറിച്ചോ ഒന്നും അറിയില്ല; അത് ആവശ്യാനുസരണം നിർത്തിവെക്കുകയും വീണ്ടും തുടങ്ങുകയും ചെയ്യുന്നു.

നിർത്തിവെച്ച ഒരു ഫൈബർ എപ്പോൾ പുനരാരംഭിക്കണം എന്ന് തീരുമാനിക്കുന്ന ഷെഡ്യൂളർ (scheduler) ആണ് ഇവന്റ് ലൂപ്പ്. ഒരു സാധാരണ അസിങ്ക് സ്റ്റാക്കിൽ (async stack), ലൂപ്പ് ഒരു കൂട്ടം ഫയൽ ഡിസ്ക്രിപ്റ്ററുകളെ നിരീക്ഷിക്കുകയും, അവ റീഡബിൾ (readable) അല്ലെങ്കിൽ റൈറ്റബിൾ (writable) ആകുന്നത് വരെ കാത്തിരിക്കുകയും, തുടർന്ന് അനുയോജ്യമായ ഫൈബറിനെ ഉണർത്തുകയും ചെയ്യുന്നു.

PHP-യിൽ മുമ്പ് Fibers ഉണ്ടായിരുന്നുവെങ്കിലും, ഏതെല്ലാം ഡിസ്ക്രിപ്റ്ററുകളാണ് തയ്യാറുള്ളതെന്ന് ഓപ്പറേറ്റിംഗ് സിസ്റ്റത്തോട് ചോദിക്കാൻ ഒരു നേറ്റീവ് സംവിധാനം ഉണ്ടായിരുന്നില്ല. ഇതിന്റെ ഫലമായി stream_select() അല്ലെങ്കിൽ എക്സ്റ്റൻഷനുകളെ ആശ്രയിക്കേണ്ടി വന്നു, കൂടാതെ ഓരോ അസിങ്ക് ലൈബ്രറിയും Linux-ന്റെ epoll, BSD-യുടെ kqueue, Windows-ന്റെ IOCP തുടങ്ങിയവയ്ക്കായി സ്വന്തമായി ബാക്ക്-എൻഡുകൾ (back-ends) എഴുതേണ്ടി വന്നു.

പുതിയ Polling API ആ വിടവ് നികത്തുന്നു. ഇത് OS-യുടെ പോളിംഗ് സൗകര്യങ്ങൾക്ക് (Linux-ൽ epoll, BSD/macOS-ൽ kqueue മുതലായവ) ചുറ്റുമുള്ള ഒരു നേരിയ റാപ്പർ (wrapper) നൽകുന്നു. ഈ API Fibers-നെ മാറ്റിസ്ഥാപിക്കുന്നില്ല; പകരം ഇവന്റ് ലൂപ്പിന് ആവശ്യമായ വേഗതയും സ്കെയിലബിലിറ്റിയും (scalability) ഇത് നൽകുന്നു.

ReactPHP, Amp എന്നിവർക്കും സുഹൃത്തുക്കൾക്കും ഈ മാറ്റം പ്രധാനമാകുന്നത് എന്തുകൊണ്ട്

ReactPHP-യും Amp v3-ഉം OS പോളിംഗ് സംവിധാനങ്ങൾക്ക് മുകളിൽ സ്വന്തമായി അബ്സ്ട്രാക്ഷൻ ലെയറുകൾ (abstraction layers) നിർമ്മിച്ചിട്ടുണ്ട്. ആ ലെയറുകളിൽ ഓരോ പ്ലാറ്റ്‌ഫോമിനും അനുയോജ്യമായ രീതിയിൽ ക്രമീകരിച്ച ഒന്നിലധികം കോഡ് പാത്തുകൾ (code paths) അടങ്ങിയിരിക്കുന്നു, കൂടാതെ അവ കേർണൽ മാറ്റങ്ങളുമായി (kernel changes) ഒത്തുപോകുന്നുണ്ടെന്ന് ഉറപ്പാക്കേണ്ടതുണ്ട്. ഒരു നേറ്റീവ് Polling API ഉള്ളതിലൂടെ, ഈ ലൈബ്രറികൾക്ക് ആ സങ്കീർണ്ണതകൾ ഒഴിവാക്കി കോർ (core) നൽകുന്ന ഒരൊറ്റ കോൾ മാത്രം ഉപയോഗിക്കാം.

  • മെയിന്റനൻസ് (Maintenance) – പ്ലാറ്റ്‌ഫോം അധിഷ്ഠിതമായ ബ്രാഞ്ചുകൾ കുറയുന്നത് ബഗുകൾ കുറയ്ക്കാനും സെക്യൂരിറ്റി റിവ്യൂകൾ എളുപ്പമാക്കാനും സഹായിക്കുന്നു.
  • പെർഫോമൻസ് (Performance) – നേറ്റീവ് കോൾ നേരിട്ട് epoll/kqueue-യുമായി ആശയവിനിമയം നടത്തുന്നു.
  • പോർട്ടബിലിറ്റി (Portability) – "vanilla" PHP-യിൽ പ്രവർത്തിക്കുന്ന കോഡിന് ഇപ്പോൾ അധിക എക്സ്റ്റൻഷനുകൾ ഇല്ലാതെ തന്നെ എല്ലാ സപ്പോർട്ട് ചെയ്യുന്ന ഓപ്പറേറ്റിംഗ് സിസ്റ്റങ്ങളിലും ഒരേ അടിസ്ഥാന പെർഫോമൻസ് ലഭിക്കുന്നു.

ഇതിനു വിപരീതമായി, Swoole എന്നത് സ്വന്തം ഇവന്റ് ലൂപ്പും കോറൂട്ടിൻ സിസ്റ്റവും (coroutine system) ഉൾക്കൊള്ളുന്ന ഒരു ഫുൾ-റൺടൈം റീപ്ലേസ്‌മെന്റ് ആയി തുടരുന്നു. Polling API, Swoole-ന്റെ പ്രവർത്തനരീതിയെ ബാധിക്കുന്നില്ല; അൾട്രാ-ലോ ലേറ്റൻസി (ultra-low latency) അല്ലെങ്കിൽ കസ്റ്റം മെമ്മറി മാനേജ്‌മെന്റ് ആവശ്യമുള്ള ഡെവലപ്പർമാർക്ക് ഇത് ഇപ്പോഴും ഒരു പ്രത്യേക ഓപ്ഷനായി പരിഗണിക്കാം.

ഒരു ചെറിയ ബെഞ്ച്മാർക്ക് ഇതിന്റെ ഗുണഫലം വ്യക്തമാക്കുന്നു

ഒരു മിനിമൽ ഷെഡ്യൂളർ, ഒന്നിലധികം ഫൈബറുകളെ നിയന്ത്രിക്കാൻ ഒരു Io\Poll\Context ഉപയോഗിച്ച് റോ സോക്കറ്റുകൾ (raw sockets) വഴി നിരവധി URL-കൾ ശേഖരിച്ചു. ഇതിൽ നിന്ന് രണ്ട് കാര്യങ്ങൾ ശ്രദ്ധിക്കപ്പെട്ടു:

  1. വേഗത (Speed) – കൂടുതൽ കോൺകറന്റ് റിക്വസ്റ്റുകൾ (concurrent requests) ചേർക്കുന്നത് ആകെ എടുക്കുന്ന സമയം വർദ്ധിപ്പിക്കുന്നില്ല; ഏറ്റവും സാവധാനം പൂർത്തിയാകുന്ന റിക്വസ്റ്റ് പോലെ തന്നെ ബാക്കിയുള്ളവയും വേഗത്തിൽ പൂർത്തിയാകുന്നു. മറ്റൊരു വിധത്തിൽ പറഞ്ഞാൽ, കോൺകറൻസി ഓവർഹെഡ് (concurrency overhead) പ്രായോഗികമായി പൂജ്യമാണ്.
  2. CPU ചിലവ് (CPU cost)stream_select() ഉപയോഗിക്കുമ്പോൾ സ്ട്രീമുകളുടെ എണ്ണം കൂടുന്തോറും CPU ഉപയോഗം ഗണ്യമായി വർദ്ധിക്കുന്നു, കാരണം ഓരോ തവണ വിളിക്കുമ്പോഴും ഫംഗ്ഷൻ എല്ലാ ഡിസ്ക്രിപ്റ്ററുകളിലൂടെയും കടന്നുപോകേണ്ടതുണ്ട്. എന്നാൽ Polling API-യുടെ കാര്യത്തിൽ, ഒരു സിസ്റ്റം കോൾ വഴി നിരവധി ഡിസ്ക്രിപ്റ്ററുകളെ നിരീക്ഷിക്കാനുള്ള കേർണലിന്റെ കഴിവ് കാരണം, മൂന്ന് സ്ട്രീമുകൾ മുതൽ മുപ്പത് സ്ട്രീമുകൾ വരെ എത്തുമ്പോഴും CPU ഉപയോഗം മാറ്റമില്ലാതെ തുടരുന്നു.

ഡെവലപ്പർമാർ ഇപ്പോൾ എന്തുചെയ്യണം

  • Amp v3 ഉപയോഗിക്കുക: പുതിയ API-യുമായി നേരിട്ട് പൊരുത്തപ്പെടുന്ന ഫൈബർ-നേറ്റീവ് ശൈലിയാണ് നിങ്ങൾ ആഗ്രഹിക്കുന്നതെങ്കിൽ ഇത് തിരഞ്ഞെടുക്കാം. ഇതിന്റെ പബ്ലിക് ഇന്റർഫേസ് നിലവിൽ തന്നെ അണ്ടർലൈയിംഗ് പോളറിലേക്ക് (underlying poller) മാപ്പ് ചെയ്തിട്ടുള്ളതിനാൽ, കോഡ് മാറ്റാതെ തന്നെ നിങ്ങൾക്ക് ഇതിന്റെ ഗുണഫലം ലഭിക്കും.
  • ReactPHP തന്നെ തുടരുക: ലൂപ്പിന്റെ ലൈഫ് സൈക്കിളിന്മേൽ (lifecycle) വ്യക്തമായ നിയന്ത്രണം വേണമെന്നുണ്ടെങ്കിൽ ഇത് തിരഞ്ഞെടുക്കാം.
  • സ്വന്തം ഷെഡ്യൂളറുകൾ ഒഴിവാക്കുക: പ്രൊഡക്ഷൻ വർക്ക് ലോഡുകൾക്കായി (production workloads) സ്വന്തം ഷെഡ്യൂളറുകൾ എഴുതരുത്.