में अनावश्यक ढेर आवंटन के कारण वैराडिक फ़ंक्शंस मैं वर्तमान में गो में कुछ प्रदर्शन संवेदनशील कोड पर काम कर रहा हूं। एक बिंदु पर मेरे पास विशेष रूप से तंग आंतरिक लूप होता है जो उत्तराधिकार में तीन चीजें करता है:गो
डेटा पर कई पॉइंटर्स प्राप्त करें। दुर्लभ त्रुटि की स्थिति में, इनमें से एक या अधिक पॉइंटर्स
nil
हो सकते हैं।जांचें कि क्या यह त्रुटि आई है, और यदि उसके पास कोई त्रुटि है तो लॉग इन करें।
पॉइंटर्स में संग्रहीत डेटा के साथ काम करें।
नीचे दिखाया गया एक ही संरचना के साथ खिलौना कार्यक्रम है (हालांकि पॉइंटर्स वास्तव में शून्य नहीं हो सकते हैं)।
package main
import (
"math/rand"
"fmt"
)
const BigScaryNumber = 1<<25
func DoWork() {
sum := 0
for i := 0; i < BigScaryNumber; i++ {
// Generate pointers.
n1, n2 := rand.Intn(20), rand.Intn(20)
ptr1, ptr2 := &n1, &n2
// Check if pointers are nil.
if ptr1 == nil || ptr2 == nil {
fmt.Printf("Pointers %v %v contain a nil.\n", ptr1, ptr2)
break
}
// Do work with pointer contents.
sum += *ptr1 + *ptr2
}
}
func main() {
DoWork()
}
जब मैं अपने मशीन पर इस चलाने के लिए, मैं निम्नलिखित मिल:
$ go build alloc.go && time ./alloc
real 0m5.466s
user 0m5.458s
sys 0m0.015s
हालांकि, अगर मैं प्रिंट बयान निकालने के लिए, मैं निम्नलिखित मिल:
$ go build alloc_no_print.go && time ./alloc_no_print
real 0m4.070s
user 0m4.063s
sys 0m0.008s
के बाद से प्रिंट स्टेटमेंट को वास्तव में कभी नहीं कहा जाता है, मैंने जांच की कि क्या प्रिंट स्टेटमेंट किसी भी तरह से स्टैक के बजाय ढेर पर पॉइंटर्स आवंटित किया गया था। मूल कार्यक्रम पर -m
ध्वज के साथ चल रहा है संकलक देता है:
$ go build -gcflags=-m alloc.go
# command-line-arguments
./alloc.go:14: moved to heap: n1
./alloc.go:15: &n1 escapes to heap
./alloc.go:14: moved to heap: n2
./alloc.go:15: &n2 escapes to heap
./alloc.go:19: DoWork ... argument does not escape
जबकि एक प्रिंट बयान कम कार्यक्रम पर यह कर
$ go build -gcflags=-m alloc_no_print.go
# command-line-arguments
./alloc_no_print.go:14: DoWork &n1 does not escape
./alloc_no_print.go:14: DoWork &n2 does not escape
की पुष्टि कर देता है कि जो भी एक अप्रयुक्त fmt.Printf()
खड़ी कर रहा है ढेर आवंटन प्रदर्शन पर एक बहुत ही वास्तविक प्रभाव। मैं एक variadic समारोह जो कुछ नहीं करता है और interface{}
रों बजाय पैरामीटर के रूप में *int
रों लेता है साथ fmt.Printf()
जगह करके समान व्यवहार प्राप्त कर सकते हैं:
func VarArgsError(ptrs ...*int) {
panic("An error has occurred.")
}
मुझे लगता है कि क्योंकि जाओ ढेर पर संकेत आवंटित जब भी वे रखा जाता है इस व्यवहार है एक टुकड़े में (हालांकि मुझे यकीन नहीं है कि यह बचने के विश्लेषण दिनचर्या का वास्तविक व्यवहार है, मुझे नहीं लगता कि यह सुरक्षित रूप से अन्यथा कैसे करने में सक्षम होगा)।
इस प्रश्न के दो उद्देश्य हैं: सबसे पहले, मैं जानना चाहता हूं कि स्थिति का मेरा विश्लेषण सही है, क्योंकि मुझे वास्तव में यह नहीं पता कि गो का भाग विश्लेषण कैसे काम करता है। और दूसरा, मैं अनियंत्रित आवंटन के बिना मूल कार्यक्रम के व्यवहार को बनाए रखने के लिए सुझाव चाहता था।
fmt.Printf("Pointers %v %v contain a nil.", Copy(ptr1), Copy(ptr2))
हालांकि यह मुझे नहीं के रूप में ही प्रदर्शन देता है जहां Copy()
रूप
func Copy(ptr *int) *int {
if ptr == nil {
return nil
} else {
n := *ptr
return &n
}
}
परिभाषित किया गया है: मेरा सर्वोत्तम अनुमान उन्हें प्रिंट बयान में गुजर करने से पहले संकेत के चारों ओर एक Copy()
समारोह रैप करने के लिए है प्रिंट कथन केस, यह अजीब है और नहीं कि मैं प्रत्येक वैरिएबल प्रकार के लिए फिर से लिखना चाहता हूं और फिर मेरे त्रुटि लॉगिंग कोड के सभी को लपेटें।
ठीक है, शुरुआत करने वालों के लिए, 'एफएमटी' पैकेज भारी फैंसी स्ट्रक्चर प्रिंटिंग प्राप्त करने के लिए प्रतिबिंब का उपयोग करता है। यदि आप वास्तव में प्रदर्शन के लिए शूटिंग कर रहे हैं तो यह एक बाधा हो सकती है। मुझे एहसास है कि इसे भी बुलाया नहीं जा रहा है - लेकिन यह सोचने के लिए कुछ और है। क्या मैं पूछ सकता हूं, क्या होता है यदि आप अपना खुद का वैरैडिक फ़ंक्शन लिखते हैं जो __NOT__ प्रकार 'इंटरफ़ेस {} 'के तर्कों को स्वीकार करता है? क्या आप एक ही मुद्दे देखते हैं? –
हां, मैंने इसे एक वैरिएडिक फ़ंक्शन पर परीक्षण किया जिसने '* int को तर्क के रूप में लिया, लेकिन यह निर्दिष्ट करना भूल गया कि स्रोत को शामिल करना है या जो मैंने अभी किया है (जिसे मैंने अभी किया है)। परिणाम 'Printf()' के समान हैं। साथ ही, आपने जिन कारणों का उल्लेख किया है, उनके लिए मैं आमतौर पर उन अनुभागों में 'fmt' पैकेज का उपयोग नहीं करता जो प्रदर्शन के लिए महत्वपूर्ण हैं। हालांकि यह निश्चित रूप से ध्यान देने योग्य एक अच्छी बात है। – mansfield
ओह, केवल मामूली रूप से सुंदर है, लेकिन दूसरा विकल्प: 'ptr1, ptr2: = ptr1, ptr2' if block के अंदर। जब तक कि कंपाइलर उस ऑप्टिमाइज़ को ऑप्टिमाइज़ नहीं करता है, अब यह 'if' से बचने वाले दो चर के रूप में घोषित किया गया है, जो 'अगर' भागने के अंदर बनाई गई 'कॉपी' से अस्थायी रूप से लौटाया जा सकता है। – twotwotwo