مرحبا،
لقد بدأت باستخدام Centrifugo في الأسبوع الماضي.
أنا أستخدم نقطة نهاية Websocket الخام التي تستخدم هذه المكتبة تحت الغطاء.
أواجه موقفًا يتم فيه تمكين تفريغ كل رسالة ، وهناك نمو هائل في الذاكرة حتى النقطة التي يتعطل فيها عامل الإرساء بسبب استخدام الكثير من الذاكرة.
أنا أعمل داخل حاوية عامل إرساء ، بمتوسط 150 ألف إلى 200 ألف مستخدم متزامن ، ومتوسط معدل الرسائل بين 30 ألف إلى 50 ألف رسالة في الثانية ، بمتوسط حجم رسالة 600 بايت.
بدون تفريغ كل رسالة ، لا توجد ذاكرة تنمو على الإطلاق والأداء رائع ، ولكن نقل البيانات مرتفع للغاية.
هل يمكن لأي شخص أن يساعدني في التحقيق فيه؟
شكرا لك.
من المحتمل أن تكون الخطوة الأولى هي الحصول على ملف تعريف كومة وتخصيص لتطبيقك باستخدام pprof.
يرجى إنشاء ملفات التعريف بأحدث إصدار من الحزمة. يجب أن يساعد التغيير الأخير في تجميع القراء والكتابات.
شكرا يا رفاق ،
أنا في انتظار الإصدار التالي من Centrifugo والذي يتضمن أحدث إصدار لديك ، ثم سأقوم بملف تعريف التطبيق وتحميله هنا.
يمكنك إغلاق هذه المشكلة حتى ذلك الحين ، لكنني آمل أن يكون الإصدار الجديد اليوم أو غدًا هو الأحدث ، لذا فالأمر متروك لك.
هنا هو تفريغ pprof
هذا هو ملف تعريف الكومة
kisielkgaryburd لقد تحديث جوهر يرجى التحقق من الآن
https://gist.github.com/joshdvir/091229e3d3e4ade8d73b8cffe86c602b
طلبت من joshdvir إرسال ملفات تعريف وحدة المعالجة المركزية والذاكرة من عقدة الإنتاج ، إليك ما لدينا:
وحدة المعالجة المركزية:
(pprof) top 20 --cum
28.99s of 62.03s total (46.74%)
Dropped 523 nodes (cum <= 0.31s)
Showing top 20 nodes out of 155 (cum >= 8.07s)
flat flat% sum% cum cum%
0 0% 0% 58.68s 94.60% runtime.goexit
0.05s 0.081% 0.081% 45.44s 73.25% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessages
0.16s 0.26% 0.34% 44.23s 71.30% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessage
0.16s 0.26% 0.6% 44.07s 71.05% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*wsSession).Send
0.05s 0.081% 0.68% 43.82s 70.64% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).WriteMessage
0.01s 0.016% 0.69% 21.67s 34.93% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*flateWriteWrapper).Close
0.03s 0.048% 0.74% 20.19s 32.55% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).NextWriter
0.07s 0.11% 0.85% 19.79s 31.90% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.compressNoContextTakeover
17.56s 28.31% 29.16% 17.56s 28.31% runtime.memclr
0.04s 0.064% 29.23% 15.46s 24.92% compress/flate.(*Writer).Reset
0.03s 0.048% 29.28% 15.42s 24.86% compress/flate.(*compressor).reset
0 0% 29.28% 14.40s 23.21% compress/flate.(*Writer).Flush
0 0% 29.28% 14.40s 23.21% compress/flate.(*compressor).syncFlush
2.62s 4.22% 33.50% 14.01s 22.59% compress/flate.(*compressor).deflate
0.01s 0.016% 33.52% 11.05s 17.81% compress/flate.(*compressor).writeBlock
0.15s 0.24% 33.76% 11.04s 17.80% compress/flate.(*huffmanBitWriter).writeBlock
0.21s 0.34% 34.10% 9.05s 14.59% runtime.systemstack
0.06s 0.097% 34.19% 8.87s 14.30% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*messageWriter).flushFrame
0.07s 0.11% 34.31% 8.81s 14.20% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).write
يقضي معظم وقت وحدة المعالجة المركزية في WriteMessage:
(pprof) list WriteMessage
Total: 1.03mins
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).WriteMessage in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/conn.go
50ms 43.82s (flat, cum) 70.64% of Total
. . 659:
. . 660:// WriteMessage is a helper method for getting a writer using NextWriter,
. . 661:// writing the message and closing the writer.
. . 662:func (c *Conn) WriteMessage(messageType int, data []byte) error {
. . 663:
50ms 50ms 664: if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
. . 665:
. . 666: // Fast path with no allocations and single frame.
. . 667:
. 20ms 668: if err := c.prepWrite(messageType); err != nil {
. . 669: return err
. . 670: }
. . 671: mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}
. 10ms 672: n := copy(c.writeBuf[mw.pos:], data)
. . 673: mw.pos += n
. . 674: data = data[n:]
. 1.69s 675: return mw.flushFrame(true, data)
. . 676: }
. . 677:
. 20.19s 678: w, err := c.NextWriter(messageType)
. . 679: if err != nil {
. . 680: return err
. . 681: }
. 190ms 682: if _, err = w.Write(data); err != nil {
. . 683: return err
. . 684: }
. 21.67s 685: return w.Close()
. . 686:}
التالي
(pprof) list NextWriter
Total: 1.03mins
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).NextWriter in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/conn.go
30ms 20.19s (flat, cum) 32.55% of Total
. . 437:// method flushes the complete message to the network.
. . 438://
. . 439:// There can be at most one open writer on a connection. NextWriter closes the
. . 440:// previous writer if the application has not already done so.
. . 441:func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
. 90ms 442: if err := c.prepWrite(messageType); err != nil {
. . 443: return nil, err
. . 444: }
. . 445:
. . 446: mw := &messageWriter{
. . 447: c: c,
. . 448: frameType: messageType,
10ms 280ms 449: pos: maxFrameHeaderSize,
. . 450: }
. 10ms 451: c.writer = mw
. . 452: if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
10ms 19.80s 453: w := c.newCompressionWriter(c.writer)
. . 454: mw.compress = true
10ms 10ms 455: c.writer = w
. . 456: }
. . 457: return c.writer, nil
. . 458:}
. . 459:
. . 460:type messageWriter struct {
CompressNoContextTakeover:
(pprof) list compressNoContextTakeover
Total: 1.03mins
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.compressNoContextTakeover in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/compression.go
70ms 19.79s (flat, cum) 31.90% of Total
. . 33: fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
. . 34: return &flateReadWrapper{fr}
. . 35:}
. . 36:
. . 37:func compressNoContextTakeover(w io.WriteCloser) io.WriteCloser {
. 130ms 38: tw := &truncWriter{w: w}
40ms 3.93s 39: fw, _ := flateWriterPool.Get().(*flate.Writer)
10ms 15.47s 40: fw.Reset(tw)
20ms 260ms 41: return &flateWriteWrapper{fw: fw, tw: tw}
. . 42:}
والآن الملف الشخصي كومة:
(pprof) top 30 --cum
4794.23MB of 5414.45MB total (88.55%)
Dropped 238 nodes (cum <= 27.07MB)
Showing top 30 nodes out of 46 (cum >= 113.64MB)
flat flat% sum% cum cum%
0 0% 0% 5385.39MB 99.46% runtime.goexit
0 0% 0% 4277.82MB 79.01% sync.(*Pool).Get
0 0% 0% 4277.82MB 79.01% sync.(*Pool).getSlow
0 0% 0% 4182.80MB 77.25% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessages
0 0% 0% 4181.80MB 77.23% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessage
0 0% 0% 4181.80MB 77.23% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*wsSession).Send
0 0% 0% 4181.80MB 77.23% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).WriteMessage
8MB 0.15% 0.15% 4168.27MB 76.98% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).NextWriter
12MB 0.22% 0.37% 4160.27MB 76.84% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.compressNoContextTakeover
3792.80MB 70.05% 70.42% 4148.27MB 76.61% compress/flate.NewWriter
0 0% 70.42% 4148.27MB 76.61% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.glob..func1
0.50MB 0.0092% 70.43% 1156.29MB 21.36% net/http.(*conn).serve
0 0% 70.43% 873.42MB 16.13% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*HTTPServer).Logged.func1
0 0% 70.43% 873.42MB 16.13% net/http.HandlerFunc.ServeHTTP
0 0% 70.43% 872.92MB 16.12% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*HTTPServer).WrapShutdown.func1
0 0% 70.43% 872.92MB 16.12% net/http.(*ServeMux).ServeHTTP
0 0% 70.43% 872.92MB 16.12% net/http.serverHandler.ServeHTTP
0 0% 70.43% 866.91MB 16.01% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*HTTPServer).RawWebsocketHandler
0 0% 70.43% 866.91MB 16.01% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*HTTPServer).RawWebsocketHandler-fm
0 0% 70.43% 404.78MB 7.48% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).ReadMessage
355.47MB 6.57% 76.99% 355.47MB 6.57% compress/flate.(*compressor).init
0 0% 76.99% 320.19MB 5.91% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Upgrader).Upgrade
0.50MB 0.0092% 77.00% 292.64MB 5.40% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).NextReader
1.50MB 0.028% 77.03% 291.64MB 5.39% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.decompressNoContextTakeover
215.85MB 3.99% 81.02% 216.35MB 4.00% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.newConn
159.10MB 2.94% 83.96% 159.10MB 2.94% compress/flate.(*decompressor).Reset
129.04MB 2.38% 86.34% 129.04MB 2.38% compress/flate.NewReader
0 0% 86.34% 129.04MB 2.38% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.glob..func2
119.46MB 2.21% 88.55% 119.46MB 2.21% net/http.newBufioWriterSize
0 0% 88.55% 113.64MB 2.10% io/ioutil.ReadAll
التالي
(pprof) list WriteMessage
Total: 5.29GB
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).WriteMessage in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/conn.go
0 4.08GB (flat, cum) 77.23% of Total
. . 673: mw.pos += n
. . 674: data = data[n:]
. . 675: return mw.flushFrame(true, data)
. . 676: }
. . 677:
. 4.07GB 678: w, err := c.NextWriter(messageType)
. . 679: if err != nil {
. . 680: return err
. . 681: }
. . 682: if _, err = w.Write(data); err != nil {
. . 683: return err
. . 684: }
. 13.53MB 685: return w.Close()
. . 686:}
CompressNoContextTakeover:
(pprof) list compressNoContextTakeover
Total: 5.29GB
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.compressNoContextTakeover in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/compression.go
12MB 4.06GB (flat, cum) 76.84% of Total
. . 33: fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
. . 34: return &flateReadWrapper{fr}
. . 35:}
. . 36:
. . 37:func compressNoContextTakeover(w io.WriteCloser) io.WriteCloser {
10MB 10MB 38: tw := &truncWriter{w: w}
. 4.05GB 39: fw, _ := flateWriterPool.Get().(*flate.Writer)
. . 40: fw.Reset(tw)
2MB 2MB 41: return &flateWriteWrapper{fw: fw, tw: tw}
. . 42:}
يحتمل أن تكون ذات صلة: https://github.com/golang/go/issues/18625
@ y3llowcake شكرا للإشارة إلى هذه المسألة.
لقد كتبت حالة اختبار لـ Gorilla Websocket:
type testConn struct {
conn *Conn
messages chan []byte
}
func newTestConn(c *Conn, bufferSize int) *testConn {
return &testConn{
conn: c,
messages: make(chan []byte, bufferSize),
}
}
func printss() {
m := runtime.MemStats{}
runtime.ReadMemStats(&m)
fmt.Printf("inuse: %d sys: %d\n", m.StackInuse, m.StackSys)
}
func TestWriteWithCompression(t *testing.T) {
w := ioutil.Discard
done := make(chan struct{})
numConns := 1000
numMessages := 1000
conns := make([]*testConn, numConns)
var wg sync.WaitGroup
for i := 0; i < numConns; i++ {
c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024)
c.enableWriteCompression = true
c.newCompressionWriter = compressNoContextTakeover
conns[i] = newTestConn(c, 256)
wg.Add(1)
go func(c *testConn) {
defer wg.Done()
i := 0
for i < numMessages {
select {
case <-done:
return
case msg := <-c.messages:
c.conn.WriteMessage(TextMessage, msg)
i++
}
}
}(conns[i])
}
messages := textMessages(100)
for i := 0; i < numMessages; i++ {
if i%100 == 0 {
printss()
}
msg := messages[i%len(messages)]
for _, c := range conns {
c.messages <- msg
}
}
wg.Wait()
}
func textMessages(num int) [][]byte {
messages := make([][]byte, num)
for i := 0; i < num; i++ {
msg := fmt.Sprintf("planet: %d, country: %d, city: %d, street: %d", i, i, i, i)
messages[i] = []byte(msg)
}
return messages
}
يقوم بإنشاء 1000 اتصال مع تمكين الضغط ، ولكل منها قناة رسائل مخزنة. ثم في حلقة نكتب رسالة في كل اتصال.
إليك كيف يتصرف مع go1.7.4
fz<strong i="7">@websocket</strong>: go test -test.run=TestWriteWithCompression
inuse: 4259840 sys: 4259840
inuse: 27394048 sys: 27394048
inuse: 246251520 sys: 246251520
inuse: 1048510464 sys: 1048510464
inuse: 1048510464 sys: 1048510464
inuse: 1049034752 sys: 1049034752
inuse: 1049034752 sys: 1049034752
inuse: 1049034752 sys: 1049034752
inuse: 1049034752 sys: 1049034752
inuse: 1049034752 sys: 1049034752
PASS
ok github.com/gorilla/websocket 11.053s
استخدام Go مع الالتزام https://github.com/golang/go/commit/9c3630f578db1d4331b367c3c7d284db299be3a6
fz<strong i="12">@websocket</strong>: go1.8 test -test.run=TestWriteWithCompression
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
inuse: 4521984 sys: 4521984
PASS
ok github.com/gorilla/websocket 12.023s
على الرغم من صعوبة القول في الوقت الحالي ، هل سيحل هذا الإصلاح المشكلة الأصلية في هذه المشكلة أم لا.
لقد جربت أيضًا نفس الشيء مع flate
من https://github.com/klauspost/compress بواسطة klauspost والذي يحتوي بالفعل على إصلاح نسخة المصفوفة هذه بشكل رئيسي:
fz<strong i="20">@websocket</strong>: go test -test.run=TestWriteWithCompression
inuse: 4358144 sys: 4358144
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
inuse: 4587520 sys: 4587520
PASS
ok github.com/gorilla/websocket 3.426s
ولكن في الواقع حتى بدون هذا الإصلاح https://github.com/klauspost/compress Library تتصرف بدون ذاكرة تنمو ... لا يمكنني شرح ذلك.
هنا أيضًا نتيجة الاختبار باستخدام
BenchmarkWriteWithCompression-4 200000 5676 ns/op 149 B/op 3 allocs/op
إنها تسريع 4x مقارنة بنتائج lib compress/flate
القياسية:
BenchmarkWriteWithCompression-4 50000 25362 ns/op 128 B/op 3 allocs/op
garyburd أفهم أن وجود حزمة lib غير قياسية في النواة قد يكون خطوة خاطئة ، ولكن ربما يمكننا التفكير في آلية للسماح بتوصيلها بطريقة ما برمز المستخدم؟
حتى بدون هذا الإصلاح يتصرف بدون أن تنمو الذاكرة ... لا يمكنني شرح ذلك.
AFAICT ، تستخدم هذه الحزمة ضغط "المستوى 3" (وهو اختيار جيد). في الحزمة الخاصة بي ، المستوى 1-4 متخصص ، ولا تستخدم الكود "العام" ، الذي يحتوي على المشكلة.
في Go 1.7 المستوى 1 (أفضل سرعة) له وظيفة متخصصة مماثلة. أعتقد أنه إذا استخدمت ذلك ، فلن تواجه المشكلة. قد يكون هذا حلاً يمكنك استخدامه ، لذلك لا يتعين عليك استيراد حزمة متخصصة (حتى لو كنت لا أمانع في منح المستخدمين الخيار). يجب أن يكون أداء المستوى 1 قريبًا جدًا من الحزمة الخاصة بي.
شكرًا klauspost على التوضيح ، فقط جربت ما قلته - نعم ، مع أداء مستوى الضغط 1 يمكن مقارنته بمكتبتك وليس لديه مشاكل في الذاكرة في go1.7 (في حالة الاختبار أعلاه)
garyburd ما رأيك في هذا؟ أرى حلين يمكنهما مساعدتنا - جعل مستوى الضغط مُصدَّرًا متغيرًا أو السماح بتوصيل تنفيذ مسطح مخصص. بالطبع يمكننا أيضًا انتظار go1.8 ولكن طريقة تحسين أداء الضغط لا تزال مهمة جدًا. هل تريد منا أن نحاول إنشاء بنية مخصصة مع النصائح التي قدمتها وخطأ نسخة مصفوفة ثابتة ونرى كيف يتصرف في الإنتاج؟
FZambia ماذا عن ضبط مستوى الضغط على واحد في الوقت الحالي؟ من المحتمل أن يكون الخيار الأفضل لمعظم التطبيقات في هذا الوقت ويتجنب تعريض المزيد من مساحة سطح API.
garyburd أتفق مع FZambia ،
عادة ، تكلف الخوادم أقل بكثير من حركة المرور عندما تكون حركة المرور عالية جدًا ، لذا فإن وجود هذا الخيار القابل للتكوين سيكون رائعًا
شكرا
حسنًا ، دعنا نضيف ما يلي:
type CompressionOptions {
// Level specifies the compression level for the flate compressor. Valid levels range from
// -2 to 9. Level -2 will use Huffman compression only. Level -1 uses the default compression
// level. Level 0 does not attempt any compression. Levels 1 through 9 range from best
// speed to best compression.
//
// Applications should set this field. The default value of 0 does not attempt any compression.
Level int
}
type Dialer struct {
// CompressionOptions specifies options the client should use for
// per message compression (RFC 7692). If CompressionOptions is nil and
// EnableCompression is nil, then the client does not attempt to negotiate
// compression with the server.
CompressionOptions *CompressionOptions
// EnableCompression specifies if the client should attempt to negotiate
// per message compression (RFC 7692). Setting this value to true does not
// guarantee that compression will be supported. Currently only "no context
// takeover" modes are supported.
//
// Deprecated: Set CompressionOptions to non-nil value to enable compression
// negotiation.
EnableCompression bool
}
تعديل مطور لتتناسب مع المسجل.
garyburd أوافق على أن المستوى 1 أفضل للقيمة الافتراضية في الوقت الحالي لأنه يعمل على إصلاح نمو الذاكرة على Go1.7 والضغط مكلف للغاية. ولكن يبدو أنه على مستوى الجماهير في تطبيقات كبيرة مثل
لقد صنعنا تصميمًا مخصصًا بمستوى الضغط 1 والعدادات التي اقترحتها ووضعها في الإنتاج. قيم العداد هي:
Node 1:
"gorilla_websocket_flate_writer_from_pool": 1453147,
"gorilla_websocket_new_flate_writer": 6702
Node 2:
"gorilla_websocket_flate_writer_from_pool": 1820919,
"gorilla_websocket_new_flate_writer": 3676,
Node 3:
"gorilla_websocket_flate_writer_from_pool": 574187,
"gorilla_websocket_new_flate_writer": 321
...
إنه تجميع أكثر من دقيقة واحدة. لذا يبدو المسبح فعالًا جدًا ولكن ...
... لا يزال الضغط هو الرائد في ملفات تخصيص التخصيص ووحدة المعالجة المركزية والحصول من المزامنة. Pool هو العملية الأكثر تكلفة للتخصيص لسبب ما.
هنا ملف تعريف وحدة المعالجة المركزية الآن:
(pprof) top 30 --cum
27.28s of 52.42s total (52.04%)
Dropped 414 nodes (cum <= 0.26s)
Showing top 30 nodes out of 137 (cum >= 1.89s)
flat flat% sum% cum cum%
0 0% 0% 50.21s 95.78% runtime.goexit
0.16s 0.31% 0.31% 43.93s 83.80% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessages
0.21s 0.4% 0.71% 42.52s 81.11% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessage
0.19s 0.36% 1.07% 42.31s 80.71% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*wsSession).Send
0.21s 0.4% 1.47% 41.87s 79.87% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).WriteMessage
0.01s 0.019% 1.49% 35.43s 67.59% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*flateWriteWrapper).Close
0.03s 0.057% 1.55% 24.69s 47.10% compress/flate.(*Writer).Flush
0 0% 1.55% 24.66s 47.04% compress/flate.(*compressor).syncFlush
0.04s 0.076% 1.62% 24.03s 45.84% compress/flate.(*compressor).encSpeed
0.08s 0.15% 1.77% 18.16s 34.64% compress/flate.(*huffmanBitWriter).writeBlockDynamic
0.12s 0.23% 2.00% 15.03s 28.67% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*messageWriter).flushFrame
0.11s 0.21% 2.21% 14.90s 28.42% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).write
0.91s 1.74% 3.95% 13.21s 25.20% compress/flate.(*huffmanEncoder).generate
0 0% 3.95% 12.72s 24.27% net.(*conn).Write
0.06s 0.11% 4.06% 12.72s 24.27% net.(*netFD).Write
11.78s 22.47% 26.54% 12.16s 23.20% syscall.Syscall
0.05s 0.095% 26.63% 12.09s 23.06% syscall.Write
0.02s 0.038% 26.67% 12.04s 22.97% syscall.write
0.61s 1.16% 27.83% 11.98s 22.85% compress/flate.(*huffmanBitWriter).indexTokens
0 0% 27.83% 10.61s 20.24% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*messageWriter).Close
5.20s 9.92% 37.75% 6.62s 12.63% compress/flate.(*huffmanEncoder).bitCounts
4.77s 9.10% 46.85% 5.44s 10.38% compress/flate.encodeBestSpeed
قائمة اكتب رسالة
(pprof) list WriteMessage
Total: 52.42s
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).WriteMessage in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/conn.go
210ms 41.87s (flat, cum) 79.87% of Total
. . 659:
. . 660:// WriteMessage is a helper method for getting a writer using NextWriter,
. . 661:// writing the message and closing the writer.
. . 662:func (c *Conn) WriteMessage(messageType int, data []byte) error {
. . 663:
160ms 160ms 664: if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
. . 665:
. . 666: // Fast path with no allocations and single frame.
. . 667:
10ms 40ms 668: if err := c.prepWrite(messageType); err != nil {
. . 669: return err
. . 670: }
. . 671: mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}
. 80ms 672: n := copy(c.writeBuf[mw.pos:], data)
. . 673: mw.pos += n
. . 674: data = data[n:]
10ms 4.43s 675: return mw.flushFrame(true, data)
. . 676: }
. . 677:
. 1.63s 678: w, err := c.NextWriter(messageType)
. . 679: if err != nil {
. . 680: return err
. . 681: }
30ms 100ms 682: if _, err = w.Write(data); err != nil {
. . 683: return err
. . 684: }
. 35.43s 685: return w.Close()
قائمة إغلاق
. . 104:func (w *flateWriteWrapper) Close() error {
. . 105: if w.fw == nil {
. . 106: return errWriteClosed
. . 107: }
. 24.69s 108: err1 := w.fw.Flush()
10ms 130ms 109: flateWriterPool.Put(w.fw)
. . 110: w.fw = nil
. . 111: if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
. . 112: return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
. . 113: }
. 10.61s 114: err2 := w.tw.w.Close()
. . 115: if err1 != nil {
. . 116: return err1
. . 117: }
. . 118: return err2
. . 119:}
قائمة دافق
. . 711:// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
. . 712:func (w *Writer) Flush() error {
. . 713: // For more about flushing:
. . 714: // http://www.bolet.org/~pornin/deflate-flush.html
30ms 24.69s 715: return w.d.syncFlush()
. . 716:}
. . 717:
. . 718:// Close flushes and closes the writer.
. . 719:func (w *Writer) Close() error {
. . 720: return w.d.close()
ROUTINE ======================== compress/flate.(*compressor).syncFlush in /Users/fz/go1.7/src/compress/flate/deflate.go
0 24.66s (flat, cum) 47.04% of Total
. . 555:func (d *compressor) syncFlush() error {
. . 556: if d.err != nil {
. . 557: return d.err
. . 558: }
. . 559: d.sync = true
. 24.03s 560: d.step(d)
. . 561: if d.err == nil {
. 490ms 562: d.w.writeStoredHeader(0, false)
. 140ms 563: d.w.flush()
. . 564: d.err = d.w.err
. . 565: }
. . 566: d.sync = false
. . 567: return d.err
. . 568:}
أصبح استخدام الذاكرة أفضل بكثير الآن ولكنه لا يزال مرتفعًا جدًا ، ويخصص الضغط الكثير:
fz<strong i="9">@centrifugo</strong>: go tool pprof --alloc_space centrifugo heap_profile_extra
Entering interactive mode (type "help" for commands)
(pprof) top 30 --cum
518.97GB of 541.65GB total (95.81%)
Dropped 314 nodes (cum <= 2.71GB)
Showing top 30 nodes out of 35 (cum >= 3.33GB)
flat flat% sum% cum cum%
0 0% 0% 541.53GB 100% runtime.goexit
0 0% 0% 505.54GB 93.33% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessages
0 0% 0% 504.45GB 93.13% github.com/centrifugal/centrifugo/libcentrifugo/conns/clientconn.(*client).sendMessage
0 0% 0% 504.45GB 93.13% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*wsSession).Send
0 0% 0% 504.45GB 93.13% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).WriteMessage
6.63GB 1.22% 1.22% 501.75GB 92.63% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).NextWriter
6.56GB 1.21% 2.44% 495.11GB 91.41% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.compressNoContextTakeover
0 0% 2.44% 491.89GB 90.81% sync.(*Pool).Get
0 0% 2.44% 491.89GB 90.81% sync.(*Pool).getSlow
359.74GB 66.42% 68.85% 488.55GB 90.20% compress/flate.NewWriter
0 0% 68.85% 488.55GB 90.20% github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.glob..func1
128.81GB 23.78% 92.63% 128.81GB 23.78% compress/flate.(*compressor).init
0.01GB 0.0019% 92.64% 28.97GB 5.35% net/http.(*conn).serve
0 0% 92.64% 25.18GB 4.65% github.com/centrifugal/centrifugo/libcentrifugo/server/httpserver.(*HTTPServer).Logged.func1
0 0% 92.64% 25.18GB 4.65% net/http.(*ServeMux).ServeHTTP
0 0% 92.64% 25.18GB 4.65% net/http.HandlerFunc.ServeHTTP
0 0% 92.64% 25.18GB 4.65% net/http.serverHandler.ServeHTTP
قائمة NextWriter
(pprof) list NextWriter
Total: 541.65GB
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.(*Conn).NextWriter in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/conn.go
6.63GB 501.75GB (flat, cum) 92.63% of Total
. . 444: }
. . 445:
. . 446: mw := &messageWriter{
. . 447: c: c,
. . 448: frameType: messageType,
6.63GB 6.63GB 449: pos: maxFrameHeaderSize,
. . 450: }
. . 451: c.writer = mw
. . 452: if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
. 495.11GB 453: w := c.newCompressionWriter(c.writer)
. . 454: mw.compress = true
. . 455: c.writer = w
. . 456: }
. . 457: return c.writer, nil
. . 458:}
قائمة CompressNoContextTakeover
(pprof) list compressNoContextTakeover
Total: 541.65GB
ROUTINE ======================== github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket.compressNoContextTakeover in /Users/fz/go/src/github.com/centrifugal/centrifugo/vendor/github.com/gorilla/websocket/compression.go
6.56GB 495.11GB (flat, cum) 91.41% of Total
. . 44: fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
. . 45: return &flateReadWrapper{fr}
. . 46:}
. . 47:
. . 48:func compressNoContextTakeover(w io.WriteCloser) io.WriteCloser {
4.41GB 4.41GB 49: tw := &truncWriter{w: w}
. 488.55GB 50: fw, _ := flateWriterPool.Get().(*flate.Writer)
. . 51: plugin.Metrics.Counters.Inc("gorilla_websocket_flate_writer_from_pool")
. . 52: fw.Reset(tw)
2.15GB 2.15GB 53: return &flateWriteWrapper{fw: fw, tw: tw}
. . 54:}
--inuse_space
صورة مماثلة بقيمتين أقل من حيث الحجم (2.69 جيجابايت لخط flateWriterPool.Get (). (* flate.Writer) الذي هو الرائد).
من الصعب أن نقول ما الذي يمكننا فعله بمثل هذا الضغط الكبير ...
garyburd تقترح إضافة CompressionOptions
- يمكنني سحب الطلب باستخدامه ، ولكن ربما مجرد متغير عالمي مُصدَّر مثل DefaultFlateCompressionLevel
الذي يمكننا تعيينه عند بدء التطبيق أو طريقة الضبط ستقوم بهذا العمل؟ لا نحتاج إلى مستوى ضغط لكل اتصال - ويمكننا في النهاية القيام بما اقترحته إذا كانت هناك حاجة لاحقًا. ولن تكون هناك حاجة إلى إهمال بهذه الطريقة في الوقت الحالي.
FZambia شكرًا لك على اختبار فعالية التجمع والإبلاغ عن الملفات الشخصية.
لا أريد إضافة واجهة برمجة تطبيقات الآن قد يتم استبدالها بواجهة برمجة تطبيقات أخرى لاحقًا.
عندما أفكر في الأمر أكثر ، من الأفضل إضافة طريقة واحدة:
// SetCompressionLevel sets the flate compression level for the next message.
// Valid levels range from -2 to 9. Level -2 will use Huffman compression only.
// Level -1 uses the default compression level. Level 0 does not attempt any
// compression. Levels 1 through 9 range from best speed to best compression.
func (c *Conn) SetCompressionLevel(n int) error {
}
هذا أكثر مرونة من الخيارات الأخرى. سيحل التطبيق محل flateWriterPool مع flatWriterPools [12]. سيحتاج flateWriterWrapper لتخزين المستوى بحيث يمكن إرجاع الكاتب إلى التجمع الصحيح.
أعتقد أن وجود طريقة واحدة تعمل على تغيير مستوى الضغط الافتراضي لا يزال أمرًا منطقيًا:
var defaultCompressionLevel int = 1
// SetDefaultCompressionLevel sets the flate compression level which will be used by
// default to compress messages when compression negotiated. This function must be
// called once before application starts.
//
// Valid levels range from -2 to 9. Level -2 will use Huffman compression only.
// Level -1 uses the default compression level. Level 0 does not attempt any
// compression. Levels 1 through 9 range from best speed to best compression.
func (c *Conn) SetDefaultCompressionLevel(n int) error {
defaultCompressionLevel = n
}
في معظم الحالات ، أفترض أن المستخدمين الذين يحتاجون إلى ضغط مخصص يحتاجون إلى تعيين هذه القيمة الافتراضية مرة واحدة.
ثم إذا احتاج شخص ما إلى مستوى ضغط لكل اتصال / رسالة ، فيمكننا إضافة SetCompressionLevel
و [12] flateWriterPool:
// SetCompressionLevel sets the flate compression level for the next message.
// Valid levels range from -2 to 9. Level -2 will use Huffman compression only.
// Level -1 uses the default compression level. Level 0 does not attempt any
// compression. Levels 1 through 9 range from best speed to best compression.
// If not set default compression level will be used.
func (c *Conn) SetCompressionLevel(n int) error {
}
إذا لم يتم استدعاؤه ولكن ضغط التفاوض سيتم استخدام defaultCompressionLevel
. التحذير الوحيد الذي أراه هو أنه يجب استدعاء SetDefaultCompressionLevel
مرة واحدة قبل أن يبدأ التطبيق في
نظرت للتو في حجم كل مثيل flate.Writer
:
package main
import "unsafe"
import "compress/flate"
func main() {
var w flate.Writer
println(unsafe.Sizeof(w))
}
600 كيلو بايت! هذا الحجم مثير للدهشة بالنسبة لي - لم أفترض حتى أنه شيء كبير :)
نعم - هناك عدد كبير من الجداول اللازمة للحفاظ على الحالة ، وإنتاج جداول huffman ومخرجات المخزن المؤقت. ليس هناك الكثير الذي يمكن القيام به حيال ذلك ، باستثناء المستوى -2 (HuffmanOnly) ، 0 (بدون ضغط) و 1 (أفضل سرعة) في المكتبة القياسية.
بالنسبة إلى مكتبتي الخاصة ، كنت أبحث عن تقليل متطلبات الذاكرة لخيارات الترميز التي لا تتطلب العديد من المخازن المؤقتة ، راجع https://github.com/klauspost/compress/pull/70 (لا يزال هناك مشكلات ، كما يمكن رؤيته من خلال الانهيار قمت بتسجيل الدخول في المشكلة)
FZambia لا أريد استخدام متغيرات مستوى الحزمة للإعدادات. يجب تضمين الإعداد مع الإعدادات الأخرى في Dialer and Upgrader أو يجب أن يكون جزءًا من الاتصال حتى يمكن تغييره من رسالة إلى أخرى.
انظر b0dc45572b148296ddf01989da00a2923d213a56.
garyburd جزيل الشكر على وقتك لمساعدتنا في هذا. بالنظر إلى كل هذه الملفات الشخصية ، هل ترى أي طريقة لتقليل استخدام الذاكرة المضغوطة والتخصيصات؟
إذا أرسل التطبيق نفس الرسالة إلى عدة عملاء ، فإن # 182 هو الشيء التالي الذي يجب تجربته.
يمكن لـgaryburd # 182 أن يفعل السحر في حالة الاستخدام الخاصة بي ، حيث يتراوح معدل خروج المعجبين بين 50 ألفًا و 90 ألفًا في الثانية حيث تكون جميع الرسائل متشابهة.
شكرا لك على مساعدتك.
هل تم إصلاح هذه المشكلة عن طريق b0dc45572b148296ddf01989da00a2923d213a56 و 804cb600d06b10672f2fbc0a336a7bee507a428e؟
كومة garyburd @ قبل التغييرات:
بعد التغييرات (مستوى الضغط 1 واستخدام PreparedMessage
):
نفس الصورة لوحدة المعالجة المركزية. لذلك لم نعد نرى ضغطًا في ملفات تعريف الكومة ووحدة المعالجة المركزية في المقام الأول بعد الآن.
على الرغم من انخفاض استخدام الذاكرة ، إلا أنه لا يزال مرتفعًا جدًا عند استخدام الضغط - ولكن قد تكون هذه مشكلة في التطبيق ، سنحاول التحقيق
garyburd فقط inuse_space
الملف الشخصي أعطانيjoshdvir .
القادة هم gorilla/websocket.newConnBRW
(36٪) ، http.newBufioWriterSize
(17٪) ، http.newBufioReader
(16٪). اعتقدت أننا استخدمنا تحسينًا حديثًا أضفته في رقم 223 ولكن بالنظر إلى الملفات الشخصية لاحظت أن مخازن القراءة والكتابة من hijack
لا يُعاد استخدامها لسبب ما. ثم وجدت خطأ في رمز Centrifugo - تم تعيين ReadBufferSize
و WriteBufferSize
على القيم الافتراضية SockJS-go
(4096) في حالة ضبط هذه الأحجام على 0 في التكوين. سيبني مع الإصلاح ويعود بالنتائج.
هناك بعض الأشياء الأخرى في الملفات الشخصية التي قد تهمك - سأحاول تحميل تصورات الرسم البياني svg من الإنشاء مع الإصلاح.
فيما يلي بعض الرسوم البيانية باستخدام Centrifugo مع أحدث Gorilla Websocket. هذا من عقدة Centrifugo التي تديرهاjoshdvir مع العديد من الوصلات (حوالي 100 كيلو) وتمكين ضغط مقبس الويب (المستوى 1) ، go1.7.5
وحدة المعالجة المركزية:
هنا نرى أن معظم وحدات المعالجة المركزية تُنفَق على syscalls - القراءة والكتابة - لا أعتقد أنه يمكننا فعل الكثير هنا ، لأننا نرسل الكثير من الرسائل الجماعية إلى مآخذ مختلفة. لذلك هذا يبدو طبيعيا.
مساحة التخصيص:
هنا نرى تأثيرًا كبيرًا لـ compress/flate.(*decompressor).Reset
- فكرت قليلاً في كيفية تحسين ذلك ، لكنني لم أجد طريقة ..
مساحة الاستخدام:
garyburd هل ترى طريقة لتحسين الأمور أكثر؟ إذا لم يكن الأمر كذلك ، فيمكننا التعايش مع هذا على ما أعتقد.
ملاحظة: سأقوم بإنشاء العديد من اتصالات ws على الجهاز المحلي مع تمكين / تعطيل الضغط ومقارنة ملفات التعريف.
لقد وجدت بعض التعديلات التي يمكن إجراؤها على وحدة فك التشفير لتجنب التخصيصات ، وتأجيل تخصيص حوالي 4 كيلوبايت إلى الوقت المطلوب بالفعل.
لا أرى أي شيء واضح للتحسين في حزمة websocket.
جربت للتو على localhost - يبدو أن استهلاك الذاكرة يأتي من أماكن مختلفة بنسب متساوية إلى حد ما ، لذلك من الصعب تشخيص الاختناق للعمل عليه. أعتقد أننا انتهينا هنا في الوقت الحالي.
klauspost لقد جربت ضغط lib الخاص بك على الإعداد المحلي أيضًا (قم بتوصيل 1000 عميل بالعقدة) - في حالتي كان استهلاك الذاكرة 50 ميجابايت بدون ضغط ، و 90 ميجابايت مع ضغط lib القياسي ، و 90 ميجابايت مع lib بدون تصحيح و 85 ميجابايت مع lib الخاص بك مع التصحيح https: //github.com/klauspost/compress/pull/76 - لا أستطيع أن أقول كيف يمكن أن يؤثر ذلك على مثيل الإنتاج (حيث أنشأت للتو العديد من الاتصالات ، بدون بث الرسائل ، بدون رسائل واردة من العملاء باستثناء الحاجة إلى الاتصال والاشتراك على قناة) - ربما تكون فائدة الذاكرة في السيناريو الحقيقي أكثر أهمية (خاصة وأننا رأينا الكثير من الذاكرة المخصصة للقراءة مضغوطة على الرسم البياني المخصص للمساحة).
أدى التحديث إلى Go1.8.1 من Go 1.7.5 إلى تحسين استخدام الذاكرة بنحو 15٪ (90 ميجابايت -> 75 ميجابايت) على الإعداد المحلي الاصطناعي الخاص بي. لكن لا يمكنني القول بالضبط من أين جاء هذا المكسب.
في مثيل الإنتاج مع تمكين الضغط ، انخفض استخدام الذاكرة أيضًا بنسبة 15-20٪ نفسها مع Go 1.8
joshdvirFZambia (وغيرها)، لا نشعر أن هذه المسألة قد عولجت مع الإصلاحات في المكتبة القياسية العودة أو لا يزال هناك شيء لتعقب هنا؟
@ checkman مرحبًا ، ليس لدي أي أفكار حول كيفية تحسين أداء الضغط بشكل أكبر. لا أعتقد أن المشكلة قد تم حلها بالكامل لأن الضغط لا يزال يؤثر على الأداء كثيرًا. ولكن إذا كنت تخطط لإغلاق هذه المشكلة - فلا تتردد في فعل ذلك لأنه لا يوجد حل واضح في الأفق. اعتمادًا على الموقف ، يمكن أن يكون PreparedMessage
منقذًا للحياة ، لكنني شخصياً لا أحب مفهومه كثيرًا لأنه محدد للغاية ويفصل دلالات كتابة البايت في الاتصال.
ربما إذا كتبت معيارًا مستقلًا إلى حد ما ، مما يعني فقط استخدام stdlib و deflate الذي يمثل سيناريوهات العالم الحقيقي ، يمكنني استخدام ذلك كمعيار للاختبار إذا كان برنامج التشفير الأقل حجمًا سيساعد بالفعل.
يجب إعادة كتابة العلاقات العامة نفسها ، ولكن وجود معيار جيد سيكون نقطة انطلاق.
يمكننا أيضا إضافة []byte
-> []byte
API، حيث يتم توفير إطار كامل من شأنها أن تقضي على ضرورة أن يكون مخزن مؤقت داخلي (وعقوبة من نسخ لذلك).
بالنظر إلى مشكلات الأداء هذه ، هل يستحق الضغط التمكين؟
يمكننا أيضًا إضافة [] بايت -> [] بايت API ، حيث يتم توفير الإطار الكامل الذي من شأنه أن يلغي الحاجة إلى وجود مخزن مؤقت داخلي (وعقوبة النسخ إليه).
كيف ستعمل واجهة برمجة التطبيقات هذه؟ هل نسمح بالكتابة مباشرة على net.Conn؟
على الخادم بدون ضغط ، يقوم برنامج WriteMessage API في الغالب بنسخ البيانات مباشرة من التطبيق الموفر [] بايت إلى اتصال الشبكة الأساسي. تقوم واجهة برمجة التطبيقات للرسالة المعدة أيضًا بنسخ [] بايت مباشرةً إلى اتصال الشبكة الأساسي.
أعتقد أن هذه المشكلة ناتجة عن ضغط / تسطيح لا يعرض طريقة لضبط النافذة المنزلقة. انظر https://golang.org/issue/3155
وبالتالي ، يخصص كل كاتب مسطح قدرًا كبيرًا من الذاكرة ، وعلى الرغم من إعادة استخدامها عبر التجمع ، فإن هذا ينتهي بالتسبب في استخدام الذاكرة الكبيرة مع الضغط على.
لا يبدو أن هناك أي شيء يمكن لهذه المكتبة أن تفعله أكثر حتى يتم إغلاق هذه المشكلة.
في الواقع ، وفقًا لهذا المعيار
func BenchmarkFlate(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
flate.NewWriter(nil, flate.BestSpeed)
}
}
$ go test -bench=BenchmarkFlate -run=^$
goos: darwin
goarch: amd64
pkg: scratch
BenchmarkFlate-8 10000 131504 ns/op 1200008 B/op 15 allocs/op
PASS
ok scratch 1.345s
flate.Newriter يخصص 1.2 ميجا بايت! هذه مساحة كبيرة من الذاكرة لتخصيصها للضغط عند كتابة رسائل WebSocket 600 بايت.
مع BestCompression أو DefaultCompress ، ينخفض التخصيص 800 كيلو بايت ولكن هذا لا يزال هائلاً.
# DefaultCompression
$ go test -bench=BenchmarkFlate -run=^$
goos: darwin
goarch: amd64
pkg: nhooyr.io/websocket
BenchmarkFlate-8 20000 93332 ns/op 806784 B/op 13 allocs/op
PASS
ok nhooyr.io/websocket 2.848s
# BestCompression
$ go test -bench=BenchmarkFlate -run=^$
goos: darwin
goarch: amd64
pkg: nhooyr.io/websocket
BenchmarkFlate-8 20000 95197 ns/op 806784 B/op 13 allocs/op
PASS
ok nhooyr.io/websocket 2.879s
لقد قدمت golang / go # 32371
ألق نظرة على https://github.com/klauspost/compress/pull/176
@ klauspost هذا رائع حقًا. ما نوع الذاكرة العلوية التي ستكون متضمنة لكل عملية كتابة؟
سوف يتطلب بضع مئات من الكيلوبايتات ، ولكن على عكس البدائل الأخرى بمجرد ضغطها ، سيتم تحرير الذاكرة.
بالنسبة لمقابس الويب ، هذا ما تحتاجه. بمعنى عدم وجود مخصصات حية للمآخذ غير النشطة. فقط تأكد من أن كل شيء مكتوب في كتابة واحدة.
klauspost شكرا ، سأحاول التحقق من ذلك بمجرد أن يكون لدي الوقت. لدي جوهر يسمح باستخدام مسطح مخصص مع مكتبة Gorilla WebSocket - أحتاج إلى إعادة النظر فيه وتجربة فرع الضغط عديم الحالة.
https://github.com/klauspost/compress/pull/185 يخفض تخصيص الكومة إلى بضع مئات من البايتات أثناء الكتابة عن طريق الحصول على sync.Pool
داخلي.
أظهر اختباري لمكتبة
راجع https://github.com/klauspost/compress/pull/216#issuecomment -586660128
حوالي 9.5 كيلوبايت مخصصة لكل رسالة مكتوبة لرسالة 512 بايت بسيطة []byte(strings.Repeat("1234", 128))
.
تحرير: متاح الآن للاختبار الرئيسي في مكتبتي.
للتوضيح ، هذا مع قاموس في وضع الاستيلاء على السياق ، إنها رسالة 50 ب مخصصة إذا لم يكن هناك قاموس.
التعليق الأكثر فائدة
لقد قدمت golang / go # 32371