Postgresql

什麼是 SFRM_Materialize_Preferred 以及如何使用它來編寫性能更高的函式?

  • November 6, 2021

研究這個問題,我看到 call 有一個SetFunctionReturnModeSFRM_Materialize_Preferred。這是什麼?這可以用來編寫性能更高的函式嗎?

有多次出現SRFM_Materializeincontrib/沒有SRFM_Materalize_Preferred

SFRM_Materialize_Preferred 允許您編寫一個函式,該函式接受來自呼叫者的提示,該提示可以從自述文件中的文件Materialize接受或ValuePerCall模式

可以同時支持ValuePerCallMaterialize模式的呼叫者將SFRM_Materialize_Preferred根據他們喜歡的模式設置或不設置。

這在原始碼中被記錄為提示- 對 - 的擴展,SFRM_Materialize

SFRM_Materialize_Random並且SFRM_Materialize_Preferred是關於模式的輔助標誌SFRM_Materialize,而不是單獨的模式。

您還可以看到這用於 SQL 的 PostgreSQL 函式管理器程式碼fmgr_sql

lazyEvalOK = !(rsi->allowedModes & SFRM_Materialize_Preferred);

我自己的示範

SFRM_Materialize_Preferred為了對其進行基準測試,我創建了自己的非常簡化的展示。你可以在我的 repo 中看到我的pg-srf-repeat. 基本上模板歸結為這一點,

Datum myfunc(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(myfunc);

typedef struct
{
 ; your function state only used in value-per-call mode
} myfunc_fctx;

Datum
myfunc(PG_FUNCTION_ARGS)
{
 
   ; get variables
   
   if (SRF_IS_FIRSTCALL())
   {
       ReturnSetInfo   *rsinfo       = (ReturnSetInfo *) fcinfo->resultinfo;

       // If we prefer materialize, get it done
       if ( rsinfo->allowedModes & SFRM_Materialize_Preferred ) {

           MemoryContext   per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
           MemoryContext   oldcontext    = MemoryContextSwitchTo(per_query_ctx);

           Tuplestorestate *tupstore     = tuplestore_begin_heap(false, false, work_mem);
           rsinfo->setResult             = tupstore;
           rsinfo->returnMode            = SFRM_Materialize;
   
           TupleDesc   tupdesc = rsinfo->expectedDesc;

           Datum values[1]             = { ;stuff };
           bool  nulls[sizeof(values)] = {0}; # no nulls

           while ( times-- ) {
               tuplestore_putvalues(tupstore, tupdesc, values, nulls);
           }

           tuplestore_donestoring(tupstore);
           MemoryContextSwitchTo(oldcontext);
   
           PG_RETURN_NULL();
       }

       // Initialize for multicall
       else {
           FuncCallContext *funcctx = SRF_FIRSTCALL_INIT();
           MemoryContext oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
           myfunc_fctx *fctx = (myfunc_fctx *) palloc(sizeof(myfunc_fctx));

           # set myfunc_fctx
           funcctx->user_fctx = fctx;
           MemoryContextSwitchTo(oldcontext);
       }

   }
   
   FuncCallContext *funcctx  = SRF_PERCALL_SETUP();
   myfunc_fctx *fctx         = funcctx->user_fctx;

   # Call this
   SRF_RETURN_NEXT(funcctx, fctx->object);
   # Or this,
   SRF_RETURN_DONE(funcctx);

}

這通常似乎執行更快的MaterializeorValuePerCall並且實現的成本很小(與純實現相比)。

在 Pg Python 中

**由於相關資訊的整體稀缺性,我在這裡保持這一點。**我已經確定這個宏SRF_SHOULD_MATERIALIZE案例實際上非常愚蠢(我已經打開了一個問題)的問題是並且始終是正確SRF_SHOULD_MATERIALIZE``SFRM_Materialize``SFRM_ValuePerCall的,因此本質上它只是SFRM_Materialize_Preferred**的較慢版本

它似乎也用於pg-python允許您從 PostgreSQL Functions 訪問 Python 3 的項目中,但害羞的是,在所有 GitHub 上確實沒有案例。for的pg-pythonSRF_SHOULD_MATERIALIZE如下(見上註)

#define SRF_SHOULD_MATERIALIZE(FCINFO) (
 (
   ((ReturnSetInfo *) FCINFO->resultinfo)->allowedModes & SFRM_Materialize
 )
 && (
   !(
     ((ReturnSetInfo *) FCINFO->resultinfo)->allowedModes & SFRM_ValuePerCall
   )
   || ((
     ((ReturnSetInfo *) FCINFO->resultinfo)->allowedModes & SFRM_Materialize_Preferred
   ))
 )
)

您可以SRF_SHOULD_MATERIALIZE在呼叫站點看到使用過一次的宏

引用自:https://dba.stackexchange.com/questions/302045