क्यों?
<ब्लॉकक्वॉट>C संस्करण इतना तेज़ क्यों है?
एक PostgreSQL सरणी अपने आप में एक बहुत ही अक्षम डेटा संरचना है। इसमें कोई भी हो सकता है डेटा प्रकार और यह बहु-आयामी होने में सक्षम है, इसलिए बहुत सारे अनुकूलन संभव नहीं हैं। हालांकि, जैसा कि आपने देखा है कि सी में एक ही सरणी के साथ बहुत तेजी से काम करना संभव है।
ऐसा इसलिए है क्योंकि सी में सरणी पहुंच पीएल/पीजीएसक्यूएल सरणी पहुंच में शामिल कई बार-बार काम से बच सकती है। बस src/backend/utils/adt/arrayfuncs.c
पर एक नज़र डालें , array_ref
. अब देखें कि इसे src/backend/executor/execQual.c
से कैसे बुलाया जाता है में ExecEvalArrayRef
. जो प्रत्येक व्यक्तिगत सरणी पहुंच . के लिए चलता है PL/PgSQL से, जैसा कि आप select pg_backend_pid()
से पाए गए pid में gdb को अटैच करके देख सकते हैं। , ExecEvalArrayRef
. पर ब्रेकप्वाइंट सेट करना , जारी रखना, और अपना कार्य चलाना।
इससे भी महत्वपूर्ण बात यह है कि पीएल/पीजीएसक्यूएल में आपके द्वारा निष्पादित प्रत्येक कथन क्वेरी निष्पादक मशीनरी के माध्यम से चलाया जाता है। यह छोटे, सस्ते बयानों को काफी धीमा कर देता है, यहां तक कि इस तथ्य की अनुमति भी देता है कि वे पहले से तैयार हैं। कुछ इस तरह:
a := b + c
वास्तव में PL/PgSQL द्वारा अधिक पसंद किया जाता है:
SELECT b + c INTO a;
आप इसे देख सकते हैं यदि आप डिबग स्तरों को पर्याप्त रूप से ऊंचा करते हैं, एक डिबगर संलग्न करते हैं और एक उपयुक्त बिंदु पर टूटते हैं, या auto_explain
का उपयोग करते हैं। नेस्टेड स्टेटमेंट विश्लेषण के साथ मॉड्यूल। आपको यह अंदाजा लगाने के लिए कि जब आप बहुत सारे छोटे-छोटे साधारण स्टेटमेंट (जैसे ऐरे एक्सेस) चला रहे हैं, तो यह कितना ओवरहेड लगाता है, इस उदाहरण पर एक नज़र डालें बैकट्रेस और इस पर मेरे नोट्स।
एक महत्वपूर्ण स्टार्ट-अप ओवरहेड . भी है प्रत्येक पीएल/पीजीएसक्यूएल फ़ंक्शन आमंत्रण के लिए। यह बहुत बड़ा नहीं है, लेकिन जब इसे समुच्चय के रूप में उपयोग किया जा रहा है तो यह जोड़ने के लिए पर्याप्त है।
सी में तेज़ तरीका
आपके मामले में मैं शायद इसे सी में करूँगा, जैसा आपने किया है, लेकिन जब मैं कुल के रूप में बुलाया जाता हूं तो मैं सरणी की प्रतिलिपि बनाने से बचूंगा। आप जांच सकते हैं कि इसे समग्र संदर्भ में लागू किया जा रहा है या नहीं:
if (AggCheckCallContext(fcinfo, NULL))
और यदि ऐसा है, तो मूल मान को एक परिवर्तनीय प्लेसहोल्डर के रूप में उपयोग करें, इसे संशोधित करें और फिर इसे एक नया आवंटित करने के बजाय इसे वापस कर दें। मैं यह सत्यापित करने के लिए एक डेमो लिखूंगा कि यह जल्द ही सरणी के साथ संभव है ... (अपडेट) या जल्द ही नहीं, मैं भूल गया कि सी में पोस्टग्रेएसक्यूएल सरणी के साथ काम करना कितना भयानक है। ये रहा:
// append to contrib/intarray/_int_op.c
PG_FUNCTION_INFO_V1(add_intarray_cols);
Datum add_intarray_cols(PG_FUNCTION_ARGS);
Datum
add_intarray_cols(PG_FUNCTION_ARGS)
{
ArrayType *a,
*b;
int i, n;
int *da,
*db;
if (PG_ARGISNULL(1))
ereport(ERROR, (errmsg("Second operand must be non-null")));
b = PG_GETARG_ARRAYTYPE_P(1);
CHECKARRVALID(b);
if (AggCheckCallContext(fcinfo, NULL))
{
// Called in aggregate context...
if (PG_ARGISNULL(0))
// ... for the first time in a run, so the state in the 1st
// argument is null. Create a state-holder array by copying the
// second input array and return it.
PG_RETURN_POINTER(copy_intArrayType(b));
else
// ... for a later invocation in the same run, so we'll modify
// the state array directly.
a = PG_GETARG_ARRAYTYPE_P(0);
}
else
{
// Not in aggregate context
if (PG_ARGISNULL(0))
ereport(ERROR, (errmsg("First operand must be non-null")));
// Copy 'a' for our result. We'll then add 'b' to it.
a = PG_GETARG_ARRAYTYPE_P_COPY(0);
CHECKARRVALID(a);
}
// This requirement could probably be lifted pretty easily:
if (ARR_NDIM(a) != 1 || ARR_NDIM(b) != 1)
ereport(ERROR, (errmsg("One-dimesional arrays are required")));
// ... as could this by assuming the un-even ends are zero, but it'd be a
// little ickier.
n = (ARR_DIMS(a))[0];
if (n != (ARR_DIMS(b))[0])
ereport(ERROR, (errmsg("Arrays are of different lengths")));
da = ARRPTR(a);
db = ARRPTR(b);
for (i = 0; i < n; i++)
{
// Fails to check for integer overflow. You should add that.
*da = *da + *db;
da++;
db++;
}
PG_RETURN_POINTER(a);
}
और इसे contrib/intarray/intarray--1.0.sql
. में जोड़ें :
CREATE FUNCTION add_intarray_cols(_int4, _int4) RETURNS _int4
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE;
CREATE AGGREGATE sum_intarray_cols(_int4) (sfunc = add_intarray_cols, stype=_int4);
(अधिक सही ढंग से आप intarray--1.1.sql
बनाएंगे और intarray--1.0--1.1.sql
और अपडेट करें intarray.control
. यह बस एक त्वरित हैक है।)
उपयोग करें:
make USE_PGXS=1
make USE_PGXS=1 install
संकलित और स्थापित करने के लिए।
अब DROP EXTENSION intarray;
(यदि आपके पास पहले से है) और CREATE EXTENSION intarray;
।
अब आपके पास समग्र कार्य होगा sum_intarray_cols
आपके लिए उपलब्ध है (जैसे आपका sum(int4[])
, साथ ही दो-संचालन add_intarray_cols
(जैसे आपका array_add
)।
पूर्णांक सरणियों में विशेषज्ञता से जटिलता का एक पूरा गुच्छा दूर हो जाता है। कुल मामले में नकल के एक समूह से बचा जाता है, क्योंकि हम "राज्य" सरणी (पहला तर्क) को सुरक्षित रूप से संशोधित कर सकते हैं। चीजों को सुसंगत रखने के लिए, गैर-समग्र आह्वान के मामले में हमें पहले तर्क की एक प्रति मिलती है ताकि हम अभी भी इसके साथ काम कर सकें और इसे वापस कर सकें।
ब्याज के प्रकार (प्रकारों) के लिए ऐड फ़ंक्शन को देखने के लिए fmgr कैश का उपयोग करके किसी भी डेटा प्रकार का समर्थन करने के लिए इस दृष्टिकोण को सामान्यीकृत किया जा सकता है। मुझे ऐसा करने में विशेष रूप से दिलचस्पी नहीं है, इसलिए यदि आपको इसकी आवश्यकता है (कहें, NUMERIC
. के स्तंभों का योग करने के लिए arrays) तो ... मज़े करो।
इसी तरह, यदि आपको असमान सरणी लंबाई को संभालने की आवश्यकता है, तो आप शायद ऊपर से पता लगा सकते हैं कि क्या करना है।