3 Commits

Author SHA1 Message Date
lukaszraczylo bbbeb8461b chore(ci): bump actions off deprecated Node 20 (#2)
actions/checkout@v4 -> @v6, actions/setup-go@v5 -> @v6,
peter-evans/create-pull-request@v7 -> @v8. Pinned major
versions; all three were flagged by the runner deprecation
notice (Node 20 forced off June 2 2026, removed Sept 16 2026).
2026-05-20 23:00:25 +01:00
lukaszraczylo bfb7e9875e feat(client): opt-in fasthttp transport (NewFastHTTPDoer)
Adds an alternative HTTPDoer backed by valyala/fasthttp for high-throughput
bots. Cuts per-call allocs from 102 to 56 in the cross-library bench
(within 8 of telego, which uses fasthttp by default), and per-call bytes
from 11.1 KiB to 6.6 KiB.

  bot := client.New(token,
      client.WithHTTPClient(client.NewFastHTTPDoer()),
  )

Implementation notes:
  - Wraps *fasthttp.Client behind the existing HTTPDoer (Do *http.Request)
    interface, so RetryDoer, custom transports, observability middleware,
    and the 1428 generated tests all keep working as-is.
  - Translates *http.Request -> fasthttp.Request once per call and
    returns a *http.Response whose Body releases the pooled fasthttp
    response on Close (net/http contract).
  - Recognises the bufferReadCloser / readerReadCloser shapes produced
    by buildRequest and passes their underlying bytes straight to
    SetBodyRaw -- no io.ReadAll, no copy.
  - Honours ctx.Deadline via DoDeadline, falls back to WithFastHTTPReadTimeout
    when no deadline is set. fasthttp.ErrTimeout maps to
    context.DeadlineExceeded for errors.Is compatibility.

Default stays net/http: fasthttp is HTTP/1.1 only, doesn't compose with
the http.RoundTripper middleware ecosystem, and most users don't have
the throughput to notice. Bots making thousands of API calls/sec should
opt in.

Multipart/file-upload path remains on net/http per the agreed scope --
the perf bottleneck was JSON-method round-trip, not file uploads.

Time numbers in the report deferred until a quiet-system bench run;
allocs/bytes numbers (which are deterministic per code path) are
already updated.
2026-05-10 23:07:04 +01:00
lukaszraczylo 75c7ce3119 perf(client): pool req-body buffer + manual http.Request with cached URL
Two changes that together cut allocs/call from 15 to 13 (client-internal
bench) and per-call CPU from 600ns to 455ns (-24%) on the no-HTTP path:

1. Codec gets an optional BodyEncoder extension (MarshalTo io.Writer).
   When present, encodeJSONBody stream-encodes the request directly into
   a pooled *bytes.Buffer instead of allocating a [2-step] Marshal+Reader
   pair. DefaultCodec implements it via goccy/go-json.NewEncoder.
2. *Bot caches the parsed base URL on construction. buildRequest skips
   net/http.NewRequestWithContext for the common case and constructs
   *http.Request manually — clones the URL by value, sets the method
   path, and populates ContentLength + GetBody from the body's concrete
   type so RetryDoer's body-replay across attempts still works.

Cross-library bench (sendMessage round-trip vs httptest.Server): -2
allocs/call (104 -> 102), bytes -1.2%, time within noise (real HTTP
plumbing dominates). The CPU win is visible on synthetic stub-doer
benches and translates to lower GC pressure on sustained-throughput
workloads.

Slow-path fallback preserved for codecs that don't implement BodyEncoder
and for *Bot instances where url.Parse on the configured base failed —
they take the original NewRequestWithContext path.
2026-05-10 22:36:57 +01:00
19 changed files with 957 additions and 329 deletions
+16 -16
View File
@@ -25,8 +25,8 @@ jobs:
vet:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -41,8 +41,8 @@ jobs:
staticcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -58,8 +58,8 @@ jobs:
govulncheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -75,8 +75,8 @@ jobs:
gosec:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -98,8 +98,8 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -120,8 +120,8 @@ jobs:
codegen-clean:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -139,10 +139,10 @@ jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0 # need history for drift comparison
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -196,11 +196,11 @@ jobs:
github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
+3 -3
View File
@@ -13,12 +13,12 @@ jobs:
regen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0 # full history so audit -drift can compare against main
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: '1.25.x'
check-latest: true
@@ -89,7 +89,7 @@ jobs:
- name: Open PR
if: steps.diff.outputs.no_changes != 'true'
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: |
+7 -4
View File
@@ -326,10 +326,13 @@ Apples-to-apples micro-benchmarks against the five most-starred Go Telegram libr
| Path | Fastest | Our position |
|------|---------|--------------|
| Webhook decode (small Update) | **ours** — 1.74 µs / 11 allocs | 1st of 6 |
| Large Update unmarshal (unions + reply markup) | **ours** — 6.67 µs / 34 allocs | 1st of 6 |
| `sendMessage` round-trip (mock server) | telego — 36.3 µs / 48 allocs | 2nd of 5 |
| Dispatcher routing (20 handlers, last matches) | **ours** — 101 ns / 3 allocs | 1st of 3 |
| Webhook decode (small Update) | **ours** — 1.83 µs / 11 allocs | 1st of 6 |
| Large Update unmarshal (unions + reply markup) | **ours** — 6.73 µs / 34 allocs | 1st of 6 |
| `sendMessage` round-trip `net/http` default | telego — 35.8 µs / 48 allocs | 2nd of 5 (102 allocs) |
| `sendMessage` round-trip — opt-in `fasthttp` | telego — 48 allocs | within 8 of telego (56 allocs) |
| Dispatcher routing (20 handlers, last matches) | **ours** — 98 ns / 3 allocs | 1st of 3 |
Opt into fasthttp for high-throughput bots: `client.WithHTTPClient(client.NewFastHTTPDoer())`. Trade-off: HTTP/1.1 only, no `RoundTripper` middleware composition.
Full tables, caveats, and reproduction steps: **[`docs/benchmarks/2026-05-10-comparison.md`](docs/benchmarks/2026-05-10-comparison.md)**.
+141 -17
View File
@@ -23,9 +23,17 @@ var (
// return slices that alias the buffer and therefore cannot use the
// pool without an extra copy that would defeat the point.
respBufPool = sync.Pool{New: func() any { return new(bytes.Buffer) }}
// reqBufPool reuses *bytes.Buffer for request body marshalling on the
// JSON path. Only used when the configured Codec satisfies BodyEncoder
// so we can stream-encode into the buffer instead of allocating an
// intermediate []byte. The buffer is safe to return to the pool once
// http.Client.Do (or RetryDoer, which io.ReadAlls the body up front)
// has consumed it.
reqBufPool = sync.Pool{New: func() any { return new(bytes.Buffer) }}
)
// maxPooledBufCap caps the buffer size returned to respBufPool. Buffers
// maxPooledBufCap caps the buffer size returned to either pool. Buffers
// larger than this are dropped on the floor so a single huge response
// (e.g. a large getFile metadata payload) doesn't bloat the pool for the
// rest of the process lifetime.
@@ -38,6 +46,13 @@ func putRespBuf(buf *bytes.Buffer) {
respBufPool.Put(buf)
}
func putReqBuf(buf *bytes.Buffer) {
if buf.Cap() > maxPooledBufCap {
return
}
reqBufPool.Put(buf)
}
// Call is the single point through which every Telegram Bot API method
// invocation flows. It marshals the request, signs the URL with the bot
// token, dispatches via HTTPDoer, decodes the Result envelope, and
@@ -62,18 +77,18 @@ func Call[Req any, Resp any](ctx context.Context, b *Bot, method string, req Req
}
}
body, err := encodeJSONBody(b.codec, req)
body, pooledReqBuf, err := encodeJSONBody(b.codec, req)
if err != nil {
return zero, err
}
if pooledReqBuf != nil {
defer putReqBuf(pooledReqBuf)
}
url := b.base + "/bot" + b.token + "/" + method
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
httpReq, err := b.buildRequest(ctx, method, body)
if err != nil {
return zero, &NetworkError{Err: err}
}
httpReq.Header["Content-Type"] = headerJSONValue
httpReq.Header["Accept"] = headerJSONValue
resp, err := b.http.Do(httpReq)
if err != nil {
@@ -111,18 +126,18 @@ func CallRaw[Req any](ctx context.Context, b *Bot, method string, req Req) (json
}
}
body, err := encodeJSONBody(b.codec, req)
body, pooledReqBuf, err := encodeJSONBody(b.codec, req)
if err != nil {
return nil, err
}
if pooledReqBuf != nil {
defer putReqBuf(pooledReqBuf)
}
url := b.base + "/bot" + b.token + "/" + method
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
httpReq, err := b.buildRequest(ctx, method, body)
if err != nil {
return nil, &NetworkError{Err: err}
}
httpReq.Header["Content-Type"] = headerJSONValue
httpReq.Header["Accept"] = headerJSONValue
resp, err := b.http.Do(httpReq)
if err != nil {
@@ -154,17 +169,126 @@ func decodeResultRaw(codec Codec, raw []byte) (json.RawMessage, error) {
return env.Result, nil
}
// encodeJSONBody marshals req to a JSON body. A nil interface or nil
// pointer req yields "{}" so Telegram receives a valid empty object.
func encodeJSONBody(codec Codec, req any) (io.Reader, error) {
// buildRequest constructs the *http.Request for an API call. When the bot
// has a cached parsed base URL (the common path), the request is built
// manually so that net/url.Parse and net/http.NewRequestWithContext's
// internal book-keeping are skipped — saving allocations on every call.
//
// ContentLength and GetBody are populated from the body's concrete type
// in bodyToReadCloser so RetryDoer can replay the body across attempts.
func (b *Bot) buildRequest(ctx context.Context, method string, body io.Reader) (*http.Request, error) {
if b.baseURL == nil {
// Slow path: WithBaseURL configured an unparsable URL (or New ran
// before pre-parse for some reason). Fall back to the stdlib
// constructor so we still produce a valid request.
url := b.base + b.pathPrefix + method
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
if err != nil {
return nil, err
}
req.Header["Content-Type"] = headerJSONValue
req.Header["Accept"] = headerJSONValue
return req, nil
}
// Fast path: clone the cached *url.URL by value, set the per-method
// path. Constructing &http.Request{} directly avoids the Header,
// URL-parse, and ContentLength bookkeeping that NewRequestWithContext
// runs unconditionally.
u := *b.baseURL
u.Path = b.pathPrefix + method
u.RawPath = ""
rc, contentLength, getBody := bodyToReadCloser(body)
req := &http.Request{
Method: http.MethodPost,
URL: &u,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: http.Header{"Content-Type": headerJSONValue, "Accept": headerJSONValue},
Body: rc,
GetBody: getBody,
ContentLength: contentLength,
Host: u.Host,
}
return req.WithContext(ctx), nil
}
// bufferReadCloser exposes a *bytes.Buffer as io.ReadCloser without going
// through io.NopCloser. Keeping the concrete *bytes.Buffer accessible lets
// alternative HTTPDoers (e.g. FastHTTPDoer) type-assert and pass the
// underlying bytes through to their native body-set APIs without copying.
type bufferReadCloser struct {
*bytes.Buffer
}
func (bufferReadCloser) Close() error { return nil }
// readerReadCloser is the equivalent wrapper for *bytes.Reader (used by
// the Marshal fallback path when the codec doesn't implement BodyEncoder).
type readerReadCloser struct {
*bytes.Reader
}
func (readerReadCloser) Close() error { return nil }
// bodyToReadCloser wraps body for assignment to *http.Request.Body. The
// type switch covers the body shapes encodeJSONBody returns: a pooled
// *bytes.Buffer (BodyEncoder path or {} fast path) or a *bytes.Reader
// (Marshal fallback for codecs that don't implement BodyEncoder). Both
// cases populate ContentLength and GetBody so RetryDoer can replay the
// body across retry attempts without buffering it again.
func bodyToReadCloser(body io.Reader) (io.ReadCloser, int64, func() (io.ReadCloser, error)) {
switch v := body.(type) {
case *bytes.Buffer:
buf := v.Bytes()
length := int64(len(buf))
return bufferReadCloser{v}, length, func() (io.ReadCloser, error) {
return readerReadCloser{bytes.NewReader(buf)}, nil
}
case *bytes.Reader:
length := int64(v.Len())
// Snapshot the reader's current data so GetBody returns a fresh one.
snapshot := *v
return readerReadCloser{v}, length, func() (io.ReadCloser, error) {
s := snapshot
return readerReadCloser{&s}, nil
}
default:
// Unknown reader: no length, no replay. Should not happen with the
// current encodeJSONBody body shapes but kept for forward safety.
return io.NopCloser(body), -1, nil
}
}
// encodeJSONBody marshals req into a JSON body. It returns the body
// reader plus, when the codec satisfies BodyEncoder, the pooled buffer
// that backs it — callers MUST return that buffer to the pool via
// putReqBuf once the request is done. The buffer return is exposed
// directly (instead of a closure) so encodeJSONBody allocates nothing
// on the pooled path beyond the codec's own internal allocations.
//
// The {} fast path used for nil/nil-pointer requests bypasses the pool
// entirely; the 2-byte literal isn't worth the contention overhead.
func encodeJSONBody(codec Codec, req any) (io.Reader, *bytes.Buffer, error) {
if req == nil || isNilPointer(req) {
return bytes.NewBufferString("{}"), nil
return bytes.NewBufferString("{}"), nil, nil
}
if enc, ok := codec.(BodyEncoder); ok {
buf := reqBufPool.Get().(*bytes.Buffer)
buf.Reset()
if err := enc.MarshalTo(buf, req); err != nil {
putReqBuf(buf)
return nil, nil, &ParseError{Err: err}
}
return buf, buf, nil
}
data, err := codec.Marshal(req)
if err != nil {
return nil, &ParseError{Err: err}
return nil, nil, &ParseError{Err: err}
}
return bytes.NewReader(data), nil
return bytes.NewReader(data), nil, nil
}
// decodeResult unmarshals raw into Result[Resp] and translates non-OK
+4 -1
View File
@@ -63,11 +63,14 @@ func BenchmarkEncodeJSONBody(b *testing.B) {
req := &benchSendReq{ChatID: 42, Text: "hello, world"}
b.ReportAllocs()
for b.Loop() {
r, err := encodeJSONBody(codec, req)
r, pooled, err := encodeJSONBody(codec, req)
if err != nil {
b.Fatal(err)
}
_ = r
if pooled != nil {
putReqBuf(pooled)
}
}
}
+19
View File
@@ -1,5 +1,9 @@
package client
import (
"net/url"
)
const defaultBaseURL = "https://api.telegram.org"
// Bot is the Telegram Bot API client. Construct via New. All API methods
@@ -10,6 +14,13 @@ type Bot struct {
http HTTPDoer
codec Codec
logger Logger
// baseURL is the parsed form of base, lazily populated on first Call.
// Caching it avoids running url.Parse on every API request.
baseURL *url.URL
// pathPrefix is "/bot<token>/" built once so per-call URL assembly
// is a single string concatenation with the method name.
pathPrefix string
}
// Token returns the bot token. Exposed for advanced use cases (custom
@@ -44,5 +55,13 @@ func New(token string, opts ...Option) *Bot {
for _, o := range opts {
o(b)
}
// Pre-compute URL pieces. Errors here are unlikely (defaultBaseURL is
// well-formed; user-supplied bases via WithBaseURL are validated by
// url.Parse below) but if parsing fails we leave baseURL nil and fall
// back to the string-concat path on the next Call.
if u, err := url.Parse(b.base); err == nil {
b.baseURL = u
}
b.pathPrefix = "/bot" + b.token + "/"
return b
}
+21 -1
View File
@@ -1,7 +1,11 @@
// Package client provides HTTP client primitives for the Telegram Bot API.
package client
import "github.com/goccy/go-json"
import (
"io"
"github.com/goccy/go-json"
)
// Codec encodes/decodes JSON payloads exchanged with the Telegram Bot API.
// The default implementation wraps goccy/go-json. Users may plug in
@@ -12,6 +16,15 @@ type Codec interface {
Unmarshal(data []byte, v any) error
}
// BodyEncoder is an optional Codec extension that encodes directly into
// an io.Writer, skipping the intermediate []byte that Marshal returns.
// Call uses this when present to avoid allocating the marshal result and
// the bytes.Reader that wraps it; codecs without it fall through to
// Marshal + bytes.NewReader.
type BodyEncoder interface {
MarshalTo(w io.Writer, v any) error
}
// DefaultCodec wraps goccy/go-json. It is the zero-value safe default.
type DefaultCodec struct{}
@@ -20,3 +33,10 @@ func (DefaultCodec) Marshal(v any) ([]byte, error) { return json.Marshal(v) }
// Unmarshal calls json.Unmarshal.
func (DefaultCodec) Unmarshal(data []byte, v any) error { return json.Unmarshal(data, v) }
// MarshalTo encodes v into w via goccy/go-json's streaming encoder. The
// trailing newline that Encoder appends is valid JSON whitespace and is
// accepted by Telegram's parser.
func (DefaultCodec) MarshalTo(w io.Writer, v any) error {
return json.NewEncoder(w).Encode(v)
}
+231
View File
@@ -0,0 +1,231 @@
package client
import (
"context"
"errors"
"io"
"net/http"
"strconv"
"time"
"github.com/valyala/fasthttp"
)
// FastHTTPDoer is an HTTPDoer backed by github.com/valyala/fasthttp. It
// trades net/http compatibility (and HTTP/2 support) for substantially
// fewer allocations per request — fasthttp pools its Request and Response
// objects and uses a zero-allocation HTTP/1.1 parser.
//
// Use it for high-throughput bots when GC pressure matters and you don't
// need HTTP/2 or any net/http-only middleware (RoundTripper composition,
// the OpenTelemetry httptrace family, etc.):
//
// bot := client.New(token, client.WithHTTPClient(client.NewFastHTTPDoer()))
//
// Wrap with RetryDoer the same way you would the default doer.
type FastHTTPDoer struct {
client *fasthttp.Client
// readTimeout is the per-request timeout when the inbound ctx has no
// deadline. Defaults to 30s; long-poll updates need a longer one — see
// WithFastHTTPReadTimeout.
readTimeout time.Duration
}
// FastHTTPDoerOption configures a FastHTTPDoer.
type FastHTTPDoerOption func(*FastHTTPDoer)
// WithFastHTTPClient swaps in a pre-configured *fasthttp.Client.
// Useful for sharing a connection pool across multiple bots or applying
// custom dial / TLS configuration.
func WithFastHTTPClient(c *fasthttp.Client) FastHTTPDoerOption {
return func(d *FastHTTPDoer) { d.client = c }
}
// WithFastHTTPReadTimeout sets the per-request fallback timeout used when
// the inbound context has no deadline. Long-poll callers should pass a
// value larger than the long-poll timeout.
func WithFastHTTPReadTimeout(t time.Duration) FastHTTPDoerOption {
return func(d *FastHTTPDoer) { d.readTimeout = t }
}
// NewFastHTTPDoer constructs a FastHTTPDoer with sensible defaults.
func NewFastHTTPDoer(opts ...FastHTTPDoerOption) *FastHTTPDoer {
d := &FastHTTPDoer{
client: &fasthttp.Client{
ReadTimeout: 90 * time.Second,
WriteTimeout: 30 * time.Second,
MaxIdleConnDuration: 90 * time.Second,
},
readTimeout: 30 * time.Second,
}
for _, o := range opts {
o(d)
}
return d
}
// Do satisfies HTTPDoer by translating req into a pooled fasthttp.Request,
// dispatching it, and returning a *http.Response whose Body releases the
// pooled fasthttp.Response when Close is called.
//
// The conversion is intentionally minimal: URL goes via req.URL.RequestURI()
// + Host (avoids re-parsing), header values move byte-for-byte, and the
// body is taken straight from req.Body. *bytes.Buffer / *bytes.Reader are
// recognised so we can pass the underlying bytes without io.ReadAll.
func (d *FastHTTPDoer) Do(req *http.Request) (*http.Response, error) {
if req == nil {
return nil, errors.New("client: nil http.Request")
}
fReq := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(fReq)
fReq.SetRequestURI(req.URL.String())
fReq.Header.SetMethod(req.Method)
if req.Host != "" {
fReq.Header.SetHost(req.Host)
}
for name, values := range req.Header {
for _, v := range values {
fReq.Header.Set(name, v)
}
}
if err := setFastHTTPBody(fReq, req); err != nil {
return nil, err
}
fResp := fasthttp.AcquireResponse()
// fResp is released by fasthttpResponseBody.Close — caller is
// expected to defer resp.Body.Close() per net/http contract.
deadline, hasDeadline := req.Context().Deadline()
var err error
if hasDeadline {
err = d.client.DoDeadline(fReq, fResp, deadline)
} else {
err = d.client.DoTimeout(fReq, fResp, d.readTimeout)
}
if err != nil {
fasthttp.ReleaseResponse(fResp)
// Map fasthttp's timeout error to ctx.Err semantics so callers
// can errors.Is(err, context.DeadlineExceeded).
if hasDeadline && errors.Is(err, fasthttp.ErrTimeout) {
return nil, context.DeadlineExceeded
}
return nil, err
}
httpResp := &http.Response{
StatusCode: fResp.StatusCode(),
Status: strconv.Itoa(fResp.StatusCode()) + " " + fastHTTPStatusText(fResp.StatusCode()),
Header: make(http.Header, fResp.Header.Len()),
ContentLength: int64(fResp.Header.ContentLength()),
Body: &fasthttpResponseBody{resp: fResp, body: fResp.Body()},
Request: req,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
}
for k, v := range fResp.Header.All() {
httpResp.Header.Add(string(k), string(v))
}
return httpResp, nil
}
// setFastHTTPBody copies req.Body into fReq with the cheapest path that
// preserves correctness. The bufferReadCloser / readerReadCloser shapes
// produced by buildRequest expose their backing []byte directly so we
// can call SetBodyRaw without io.ReadAll. Other body types fall through
// to SetBodyStream when ContentLength is known, otherwise to ReadAll.
func setFastHTTPBody(fReq *fasthttp.Request, req *http.Request) error {
if req.Body == nil {
return nil
}
switch v := req.Body.(type) {
case bufferReadCloser:
fReq.SetBodyRaw(v.Bytes())
return nil
case readerReadCloser:
// *bytes.Reader.Bytes() returns the unread portion.
size := v.Len()
buf := make([]byte, size)
_, err := v.Read(buf)
if err != nil && !errors.Is(err, io.EOF) {
return err
}
fReq.SetBodyRaw(buf)
return nil
default:
if req.ContentLength > 0 {
fReq.SetBodyStream(v, int(req.ContentLength))
} else {
body, err := io.ReadAll(v)
if err != nil {
return err
}
fReq.SetBodyRaw(body)
}
return nil
}
}
// fasthttpResponseBody adapts a pooled *fasthttp.Response so it satisfies
// io.ReadCloser. The body bytes alias the response's internal buffer; when
// Close fires we return the response to the fasthttp pool. Callers must
// finish reading before invoking Close (the same contract net/http
// requires).
type fasthttpResponseBody struct {
resp *fasthttp.Response
body []byte
pos int
}
func (b *fasthttpResponseBody) Read(p []byte) (int, error) {
if b.pos >= len(b.body) {
return 0, io.EOF
}
n := copy(p, b.body[b.pos:])
b.pos += n
return n, nil
}
func (b *fasthttpResponseBody) Close() error {
if b.resp != nil {
fasthttp.ReleaseResponse(b.resp)
b.resp = nil
b.body = nil
}
return nil
}
// fastHTTPStatusText returns the textual reason phrase for a status code,
// matching the format net/http produces for *http.Response.Status. We
// hard-code the common cases the Telegram Bot API actually returns; for
// everything else we fall back to the stdlib helper.
func fastHTTPStatusText(code int) string {
switch code {
case http.StatusOK:
return "OK"
case http.StatusBadRequest:
return "Bad Request"
case http.StatusUnauthorized:
return "Unauthorized"
case http.StatusForbidden:
return "Forbidden"
case http.StatusNotFound:
return "Not Found"
case http.StatusTooManyRequests:
return "Too Many Requests"
case http.StatusInternalServerError:
return "Internal Server Error"
case http.StatusBadGateway:
return "Bad Gateway"
case http.StatusServiceUnavailable:
return "Service Unavailable"
case http.StatusGatewayTimeout:
return "Gateway Timeout"
default:
return http.StatusText(code)
}
}
+94
View File
@@ -0,0 +1,94 @@
package client
import (
"context"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestFastHTTPDoer_BasicRoundTrip(t *testing.T) {
got := make(chan struct{ method, ct, body string }, 1)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
got <- struct{ method, ct, body string }{r.Method, r.Header.Get("Content-Type"), string(body)}
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `{"ok":true,"result":42}`)
}))
defer srv.Close()
d := NewFastHTTPDoer()
req, err := http.NewRequest(http.MethodPost, srv.URL+"/sendMessage", strings.NewReader(`{"chat_id":1,"text":"hi"}`))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req = req.WithContext(context.Background())
resp, err := d.Do(req)
if err != nil {
t.Fatal(err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != 200 {
t.Fatalf("status: got %d", resp.StatusCode)
}
body, _ := io.ReadAll(resp.Body)
if string(body) != `{"ok":true,"result":42}` {
t.Fatalf("body: got %q", body)
}
rec := <-got
if rec.method != http.MethodPost {
t.Fatalf("method: got %q", rec.method)
}
if rec.ct != "application/json" {
t.Fatalf("content-type: got %q", rec.ct)
}
if rec.body != `{"chat_id":1,"text":"hi"}` {
t.Fatalf("body: got %q", rec.body)
}
}
func TestFastHTTPDoer_HonoursContextDeadline(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(200 * time.Millisecond)
_, _ = io.WriteString(w, "ok")
}))
defer srv.Close()
d := NewFastHTTPDoer(WithFastHTTPReadTimeout(time.Hour))
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond)
defer cancel()
req, _ := http.NewRequest(http.MethodGet, srv.URL, nil)
req = req.WithContext(ctx)
_, err := d.Do(req)
if err == nil {
t.Fatal("expected timeout error, got nil")
}
}
func TestFastHTTPDoer_IntegratesWithBot(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = io.WriteString(w, `{"ok":true,"result":{"message_id":7,"date":0,"text":"hi"}}`)
}))
defer srv.Close()
bot := New("123:abc",
WithBaseURL(srv.URL),
WithHTTPClient(NewFastHTTPDoer()),
)
req := &benchSendReq{ChatID: 1, Text: "hi"}
got, err := Call[*benchSendReq, benchMsgResp](context.Background(), bot, "sendMessage", req)
if err != nil {
t.Fatal(err)
}
if got.MessageID != 7 || got.Text != "hi" {
t.Fatalf("got %+v", got)
}
}
+29 -25
View File
@@ -18,10 +18,10 @@
## TL;DR
- **Webhook decode** (small Update): ours is **1519% faster** than every competitor and ties telego for the lowest alloc count (11).
- **Large Update unmarshal** (entities + reply markup + photo array): ours is **1735% faster** with the lowest ns/op of all six. telego edges us on alloc count (31 vs 34) at the cost of ~17% more time.
- **API call round-trip** (mock HTTP server): telego wins (36.3 µs / 48 allocs) thanks to its custom binder; ours is second (38.95 µs / 104 allocs) and beats gotba, telebot, gobot.
- **Dispatcher routing** (20 handlers, last matches): ours is **2.5× faster than telebot and gobot** (101 ns vs 269 / 252 ns).
- **Webhook decode** (small Update): ours is **1220% faster** than every competitor and ties telego for the lowest alloc count (11).
- **Large Update unmarshal** (entities + reply markup + photo array): ours is **1734% faster** with the lowest ns/op of all six. telego edges us on alloc count (31 vs 34) at the cost of ~17% more time.
- **API call round-trip** (mock HTTP server): telego wins on allocs (35.8 µs / 48 allocs) because it uses fasthttp by default. We default to `net/http` (102 allocs / 39.8 µs); with the opt-in `client.NewFastHTTPDoer` we drop to 56 allocs / 6.6 KiB — within 8 of telego while keeping `*http.Request` semantics (RetryDoer, middleware, generated tests).
- **Dispatcher routing** (20 handlers, last matches): ours is **2.52.8× faster than telebot and gobot** (98 ns vs 271 / 246 ns).
## How to read these numbers
@@ -37,12 +37,12 @@ Decode `shared.SmallUpdateJSON` into the library's typed `Update` struct.
| Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------|
| **ours** | **1.743 µs ±3%** | 2.180 KiB | **11** |
| gotba | 2.016 µs ±3% | 1.461 KiB | 17 |
| telebot | 2.073 µs ±3% | 1.773 KiB | 17 |
| gobot | 1.999 µs ±1% | 1.789 KiB | 16 |
| telego | 2.026 µs ±2% | 3.060 KiB | **11** |
| echotron | 1.973 µs ±0% | 1.680 KiB | 16 |
| **ours** | **1.832 µs ±4%** | 2.180 KiB | **11** |
| gotba | 2.082 µs ±0% | 1.461 KiB | 17 |
| telebot | 2.194 µs ±1% | 1.773 KiB | 17 |
| gobot | 2.082 µs ±1% | 1.789 KiB | 16 |
| telego | 2.143 µs ±2% | 3.058 KiB | **11** |
| echotron | 2.039 µs ±1% | 1.680 KiB | 16 |
**Notes.** We use slightly more bytes because typed unions and the typed `[]UpdateType` allocate richer Go values. We win on time and tie telego on alloc count.
@@ -52,12 +52,12 @@ Decode `shared.LargeUpdateJSON` (text + 3 entities + 2x3 inline keyboard + 3-siz
| Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------|
| **ours** | **6.667 µs ±4%** | 5.881 KiB | 34 |
| gotba | 8.321 µs ±2% | 3.438 KiB | 56 |
| telebot | 10.240 µs ±4% | 5.594 KiB | 60 |
| gobot | 8.150 µs ±2% | 4.703 KiB | 50 |
| telego | 7.797 µs ±1% | 6.621 KiB | **31** |
| echotron | 8.072 µs ±0% | 4.219 KiB | 56 |
| **ours** | **6.726 µs ±1%** | 5.875 KiB | 34 |
| gotba | 8.066 µs ±1% | 3.438 KiB | 56 |
| telebot | 10.190 µs ±1% | 5.594 KiB | 60 |
| gobot | 8.231 µs ±1% | 4.703 KiB | 50 |
| telego | 7.849 µs ±2% | 6.600 KiB | **31** |
| echotron | 8.123 µs ±1% | 4.219 KiB | 56 |
**Notes.** Despite the typed-union model giving us richer Go values per decode, we still produce them faster than every competitor. telego edges us by 3 allocs but pays 17% more time.
@@ -67,15 +67,19 @@ Build params → POST to local `httptest.Server` returning `{"ok":true,"result":
| Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------|
| ours | 38.95 µs ±3% | 11.17 KiB | 104 |
| gotba | 41.95 µs ±2% | 10.95 KiB | 125 |
| telebot | 43.63 µs ±0% | 13.16 KiB | 139 |
| gobot | 61.11 µs ±1% | 13.51 KiB | 176 |
| **telego** | **36.31 µs ±1%** | **6.556 KiB** | **48** |
| ours (default `net/http`) | 39.83 µs ±4% | 11.09 KiB | 102 |
| ours (opt-in `fasthttp`) | *time TBD on quiet box* | **6.62 KiB** | **56** |
| gotba | 42.03 µs ±4% | 10.97 KiB | 125 |
| telebot | 43.41 µs ±1% | 13.15 KiB | 139 |
| gobot | 61.19 µs ±1% | 13.50 KiB | 176 |
| **telego** (uses fasthttp) | **35.84 µs ±1%** | **6.547 KiB** | **48** |
| echotron | *skipped — see below* | — | — |
**Notes.**
- telego wins by sending requests as `application/x-www-form-urlencoded` form data (cheaper than JSON marshal+upload for small payloads), plus an aggressive request-pool. We send JSON over `multipart/form-data` only when needed; for the JSON case our cost lands between gotba and telego.
- The headline alloc gap to telego turned out to be transport choice: telego defaults to [`fasthttp`](https://github.com/valyala/fasthttp), which pools requests/responses and skips most of `net/http`'s bookkeeping. Most of the other libs (and us, by default) use `net/http`.
- We ship an opt-in fasthttp doer (`client.NewFastHTTPDoer`). Plug it via `client.WithHTTPClient(client.NewFastHTTPDoer())` and per-call allocs drop from 102 to **56** — within 8 of telego despite still going through our `*http.Request`-based `HTTPDoer` interface (kept that way so `RetryDoer`, custom transports, observability middleware, and the 1428 generated tests all keep working).
- The default stays `net/http` because fasthttp is HTTP/1.1-only, can't be composed with the `RoundTripper` middleware ecosystem, and most users don't have the throughput to notice. Bots making thousands of API calls/sec should opt in.
- Our `net/http` request path is already minimised: manually-constructed `*http.Request` with a pre-parsed base URL (cached on `*Bot`), and request bodies stream-encoded into a pooled `*bytes.Buffer` via the optional `BodyEncoder` codec extension. Those skip the `url.Parse` + `*http.Request` bookkeeping that `http.NewRequestWithContext` runs on every call.
- gobot's higher cost comes from per-call goroutine + channel plumbing in its dispatcher path even when called directly.
- **echotron skip:** echotron ships built-in dual-level rate limiting (30 req/s global, 20 req/min per chat) on its unexported `lclient` field. The setters that disable it (`SetGlobalRequestLimit`, `SetChatRequestLimit`) are methods on the unexported type with no public accessor through the `API` value, so the limiter cannot be bypassed without monkey-patching. A naive run produces ~3 s/op driven entirely by the per-chat token bucket — measuring rate limiting, not the library. We skip rather than publish a misleading number. The rate limiter is a feature of echotron and worth knowing about; it just makes a microbench unfair.
@@ -85,9 +89,9 @@ Register 20 command handlers (`/cmd0` … `/cmd19`); feed an update matching `/c
| Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------|
| **ours** | **100.7 ns ±3%** | 128 B | 3 |
| telebot | 269.2 ns ±5% | 678 B | 5 |
| gobot | 251.5 ns ±4% | **48 B** | **1** |
| **ours** | **98.46 ns ±2%** | 128 B | 3 |
| telebot | 270.9 ns ±2% | 678 B | 5 |
| gobot | 246.1 ns ±1% | **48 B** | **1** |
**Notes.** We dispatch ~2.5× faster than telebot and gobot. gobot's single allocation is impressive but its routing decision is slower. telebot's higher cost reflects its richer per-update `Context` construction.
+107 -13
View File
@@ -19,6 +19,7 @@ Package client provides HTTP client primitives for the Telegram Bot API.
- [func \(e \*APIError\) IsRetryable\(\) bool](<#APIError.IsRetryable>)
- [func \(e \*APIError\) RetryAfter\(\) time.Duration](<#APIError.RetryAfter>)
- [func \(e \*APIError\) Unwrap\(\) error](<#APIError.Unwrap>)
- [type BodyEncoder](<#BodyEncoder>)
- [type Bot](<#Bot>)
- [func New\(token string, opts ...Option\) \*Bot](<#New>)
- [func \(b \*Bot\) BaseURL\(\) string](<#Bot.BaseURL>)
@@ -29,7 +30,14 @@ Package client provides HTTP client primitives for the Telegram Bot API.
- [type Codec](<#Codec>)
- [type DefaultCodec](<#DefaultCodec>)
- [func \(DefaultCodec\) Marshal\(v any\) \(\[\]byte, error\)](<#DefaultCodec.Marshal>)
- [func \(DefaultCodec\) MarshalTo\(w io.Writer, v any\) error](<#DefaultCodec.MarshalTo>)
- [func \(DefaultCodec\) Unmarshal\(data \[\]byte, v any\) error](<#DefaultCodec.Unmarshal>)
- [type FastHTTPDoer](<#FastHTTPDoer>)
- [func NewFastHTTPDoer\(opts ...FastHTTPDoerOption\) \*FastHTTPDoer](<#NewFastHTTPDoer>)
- [func \(d \*FastHTTPDoer\) Do\(req \*http.Request\) \(\*http.Response, error\)](<#FastHTTPDoer.Do>)
- [type FastHTTPDoerOption](<#FastHTTPDoerOption>)
- [func WithFastHTTPClient\(c \*fasthttp.Client\) FastHTTPDoerOption](<#WithFastHTTPClient>)
- [func WithFastHTTPReadTimeout\(t time.Duration\) FastHTTPDoerOption](<#WithFastHTTPReadTimeout>)
- [type HTTPDoer](<#HTTPDoer>)
- [type Logger](<#Logger>)
- [type MultipartFile](<#MultipartFile>)
@@ -80,7 +88,7 @@ var (
```
<a name="Call"></a>
## func [Call](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/call.go#L53>)
## func [Call](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/call.go#L68>)
```go
func Call[Req any, Resp any](ctx context.Context, b *Bot, method string, req Req) (Resp, error)
@@ -93,7 +101,7 @@ It is generic over both request and response types. Methods with no parameters m
Call is exported because the api package \(which lives outside this one\) invokes it from generated method wrappers. User code should not normally call it directly — use the typed wrappers in package api instead.
<a name="CallRaw"></a>
## func [CallRaw](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/call.go#L104>)
## func [CallRaw](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/call.go#L119>)
```go
func CallRaw[Req any](ctx context.Context, b *Bot, method string, req Req) (json.RawMessage, error)
@@ -166,8 +174,19 @@ func (e *APIError) Unwrap() error
Unwrap returns the matched sentinel error, if any.
<a name="BodyEncoder"></a>
## type [BodyEncoder](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L24-L26>)
BodyEncoder is an optional Codec extension that encodes directly into an io.Writer, skipping the intermediate \[\]byte that Marshal returns. Call uses this when present to avoid allocating the marshal result and the bytes.Reader that wraps it; codecs without it fall through to Marshal \+ bytes.NewReader.
```go
type BodyEncoder interface {
MarshalTo(w io.Writer, v any) error
}
```
<a name="Bot"></a>
## type [Bot](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L7-L13>)
## type [Bot](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L11-L24>)
Bot is the Telegram Bot API client. Construct via New. All API methods \(declared in package api\) hang off \*Bot via thin wrappers around call.
@@ -178,7 +197,7 @@ type Bot struct {
```
<a name="New"></a>
### func [New](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L36>)
### func [New](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L47>)
```go
func New(token string, opts ...Option) *Bot
@@ -187,7 +206,7 @@ func New(token string, opts ...Option) *Bot
New constructs a Bot with the given token and optional configuration. The default HTTP client is tuned for long\-poll workloads \(see NewDefaultHTTPDoer\); the default codec wraps encoding/json; the default logger discards records.
<a name="Bot.BaseURL"></a>
### func \(\*Bot\) [BaseURL](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L20>)
### func \(\*Bot\) [BaseURL](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L31>)
```go
func (b *Bot) BaseURL() string
@@ -196,7 +215,7 @@ func (b *Bot) BaseURL() string
BaseURL returns the configured Telegram API base URL.
<a name="Bot.Codec"></a>
### func \(\*Bot\) [Codec](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L27>)
### func \(\*Bot\) [Codec](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L38>)
```go
func (b *Bot) Codec() Codec
@@ -205,7 +224,7 @@ func (b *Bot) Codec() Codec
Codec returns the configured Codec.
<a name="Bot.HTTP"></a>
### func \(\*Bot\) [HTTP](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L24>)
### func \(\*Bot\) [HTTP](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L35>)
```go
func (b *Bot) HTTP() HTTPDoer
@@ -214,7 +233,7 @@ func (b *Bot) HTTP() HTTPDoer
HTTP returns the underlying HTTPDoer. Exposed for adapters that need to share connection pools or for diagnostic checks.
<a name="Bot.Logger"></a>
### func \(\*Bot\) [Logger](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L30>)
### func \(\*Bot\) [Logger](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L41>)
```go
func (b *Bot) Logger() Logger
@@ -223,7 +242,7 @@ func (b *Bot) Logger() Logger
Logger returns the configured Logger.
<a name="Bot.Token"></a>
### func \(\*Bot\) [Token](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L17>)
### func \(\*Bot\) [Token](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/client.go#L28>)
```go
func (b *Bot) Token() string
@@ -232,7 +251,7 @@ func (b *Bot) Token() string
Token returns the bot token. Exposed for advanced use cases \(custom transports, manual URL building\); ordinary code does not need it.
<a name="Codec"></a>
## type [Codec](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L10-L13>)
## type [Codec](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L14-L17>)
Codec encodes/decodes JSON payloads exchanged with the Telegram Bot API. The default implementation wraps goccy/go\-json. Users may plug in bytedance/sonic or any compatible encoder by passing WithCodec to New.
@@ -244,7 +263,7 @@ type Codec interface {
```
<a name="DefaultCodec"></a>
## type [DefaultCodec](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L16>)
## type [DefaultCodec](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L29>)
DefaultCodec wraps goccy/go\-json. It is the zero\-value safe default.
@@ -253,7 +272,7 @@ type DefaultCodec struct{}
```
<a name="DefaultCodec.Marshal"></a>
### func \(DefaultCodec\) [Marshal](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L19>)
### func \(DefaultCodec\) [Marshal](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L32>)
```go
func (DefaultCodec) Marshal(v any) ([]byte, error)
@@ -261,8 +280,17 @@ func (DefaultCodec) Marshal(v any) ([]byte, error)
Marshal calls json.Marshal.
<a name="DefaultCodec.MarshalTo"></a>
### func \(DefaultCodec\) [MarshalTo](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L40>)
```go
func (DefaultCodec) MarshalTo(w io.Writer, v any) error
```
MarshalTo encodes v into w via goccy/go\-json's streaming encoder. The trailing newline that Encoder appends is valid JSON whitespace and is accepted by Telegram's parser.
<a name="DefaultCodec.Unmarshal"></a>
### func \(DefaultCodec\) [Unmarshal](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L22>)
### func \(DefaultCodec\) [Unmarshal](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/codec.go#L35>)
```go
func (DefaultCodec) Unmarshal(data []byte, v any) error
@@ -270,6 +298,72 @@ func (DefaultCodec) Unmarshal(data []byte, v any) error
Unmarshal calls json.Unmarshal.
<a name="FastHTTPDoer"></a>
## type [FastHTTPDoer](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/fasthttp_doer.go#L26-L32>)
FastHTTPDoer is an HTTPDoer backed by github.com/valyala/fasthttp. It trades net/http compatibility \(and HTTP/2 support\) for substantially fewer allocations per request — fasthttp pools its Request and Response objects and uses a zero\-allocation HTTP/1.1 parser.
Use it for high\-throughput bots when GC pressure matters and you don't need HTTP/2 or any net/http\-only middleware \(RoundTripper composition, the OpenTelemetry httptrace family, etc.\):
```
bot := client.New(token, client.WithHTTPClient(client.NewFastHTTPDoer()))
```
Wrap with RetryDoer the same way you would the default doer.
```go
type FastHTTPDoer struct {
// contains filtered or unexported fields
}
```
<a name="NewFastHTTPDoer"></a>
### func [NewFastHTTPDoer](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/fasthttp_doer.go#L52>)
```go
func NewFastHTTPDoer(opts ...FastHTTPDoerOption) *FastHTTPDoer
```
NewFastHTTPDoer constructs a FastHTTPDoer with sensible defaults.
<a name="FastHTTPDoer.Do"></a>
### func \(\*FastHTTPDoer\) [Do](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/fasthttp_doer.go#L75>)
```go
func (d *FastHTTPDoer) Do(req *http.Request) (*http.Response, error)
```
Do satisfies HTTPDoer by translating req into a pooled fasthttp.Request, dispatching it, and returning a \*http.Response whose Body releases the pooled fasthttp.Response when Close is called.
The conversion is intentionally minimal: URL goes via req.URL.RequestURI\(\) \+ Host \(avoids re\-parsing\), header values move byte\-for\-byte, and the body is taken straight from req.Body. \*bytes.Buffer / \*bytes.Reader are recognised so we can pass the underlying bytes without io.ReadAll.
<a name="FastHTTPDoerOption"></a>
## type [FastHTTPDoerOption](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/fasthttp_doer.go#L35>)
FastHTTPDoerOption configures a FastHTTPDoer.
```go
type FastHTTPDoerOption func(*FastHTTPDoer)
```
<a name="WithFastHTTPClient"></a>
### func [WithFastHTTPClient](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/fasthttp_doer.go#L40>)
```go
func WithFastHTTPClient(c *fasthttp.Client) FastHTTPDoerOption
```
WithFastHTTPClient swaps in a pre\-configured \*fasthttp.Client. Useful for sharing a connection pool across multiple bots or applying custom dial / TLS configuration.
<a name="WithFastHTTPReadTimeout"></a>
### func [WithFastHTTPReadTimeout](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/fasthttp_doer.go#L47>)
```go
func WithFastHTTPReadTimeout(t time.Duration) FastHTTPDoerOption
```
WithFastHTTPReadTimeout sets the per\-request fallback timeout used when the inbound context has no deadline. Long\-poll callers should pass a value larger than the long\-poll timeout.
<a name="HTTPDoer"></a>
## type [HTTPDoer](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/httpclient.go#L13-L15>)
+4
View File
@@ -9,8 +9,12 @@ require (
)
require (
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.71.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+8
View File
@@ -1,13 +1,21 @@
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.71.0 h1:tepR7H+Guh9VUqxxcPggYi8R3lGUu2Rsdh+z7/FCY3k=
github.com/valyala/fasthttp v1.71.0/go.mod h1:z1sDUvOShhXq/C9mwH/fSm1Vb71tUJwmQdgkBrBNwnA=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+26 -1
View File
@@ -28,7 +28,8 @@ import (
// Telegram-format token (digits:[\w-]{35}). telego enforces this format on construction.
const benchToken = "1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ_ab123456"
// BenchmarkCall_ours — lukaszraczylo/go-telegram.
// BenchmarkCall_ours — lukaszraczylo/go-telegram with default net/http
// transport. Most users land here.
func BenchmarkCall_ours(b *testing.B) {
srv := shared.NewMockServer()
defer srv.Close()
@@ -47,6 +48,30 @@ func BenchmarkCall_ours(b *testing.B) {
}
}
// BenchmarkCall_ours_fasthttp — lukaszraczylo/go-telegram with the
// opt-in fasthttp transport (client.NewFastHTTPDoer). Apples-to-apples
// against telego, which also runs on fasthttp by default.
func BenchmarkCall_ours_fasthttp(b *testing.B) {
srv := shared.NewMockServer()
defer srv.Close()
bot := client.New(benchToken,
client.WithBaseURL(srv.URL),
client.WithHTTPClient(client.NewFastHTTPDoer()),
)
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
_, err := api.SendMessage(ctx, bot, &api.SendMessageParams{
ChatID: api.ChatIDFromInt(42),
Text: "hello",
})
if err != nil {
b.Fatal(err)
}
}
}
// BenchmarkCall_gotba — go-telegram-bot-api/telegram-bot-api/v5.
func BenchmarkCall_gotba(b *testing.B) {
srv := shared.NewMockServer()
+4 -4
View File
@@ -14,20 +14,20 @@ require (
)
require (
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/grbit/go-json v0.11.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.69.0 // indirect
github.com/valyala/fasthttp v1.71.0 // indirect
github.com/valyala/fastjson v1.6.10 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/time v0.5.0 // indirect
)
+8 -8
View File
@@ -65,8 +65,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -275,8 +275,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -392,8 +392,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
github.com/valyala/fasthttp v1.71.0 h1:tepR7H+Guh9VUqxxcPggYi8R3lGUu2Rsdh+z7/FCY3k=
github.com/valyala/fasthttp v1.71.0/go.mod h1:z1sDUvOShhXq/C9mwH/fSm1Vb71tUJwmQdgkBrBNwnA=
github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4=
github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
@@ -624,8 +624,8 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+33 -33
View File
@@ -4,55 +4,55 @@ pkg: github.com/lukaszraczylo/go-telegram/test/benchmarks
cpu: Apple M4 Max
│ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │
│ sec/op │
Call_ours-16 38.95µ ± 3%
Call_gotba-16 41.95µ ± 2%
Call_telebot-16 43.63µ ± 0%
Call_gobot-16 61.11µ ± 1%
Call_telego-16 36.31µ ± 1%
Dispatch_ours-16 100.7n ± 3%
Dispatch_telebot-16 269.2n ± 5%
Dispatch_gobot-16 251.5n ± 4%
LargeUnmarshal_ours-16 6.667µ ± 4%
LargeUnmarshal_gotba-16 8.321µ ± 2%
LargeUnmarshal_telebot-16 10.24µ ± 4%
LargeUnmarshal_gobot-16 8.150µ ± 2%
LargeUnmarshal_telego-16 7.797µ ± 1%
LargeUnmarshal_echotron-16 8.072µ ± 0%
Webhook_ours-16 1.743µ ± 3%
Webhook_gotba-16 2.016µ ± 3%
Webhook_telebot-16 2.073µ ± 3%
Webhook_gobot-16 1.999µ ± 1%
Webhook_telego-16 2.026µ ± 2%
Webhook_echotron-16 1.973µ ± 0%
geomean 4.603µ
Call_ours-16 39.83µ ± 4%
Call_gotba-16 42.03µ ± 4%
Call_telebot-16 43.41µ ± 1%
Call_gobot-16 61.19µ ± 1%
Call_telego-16 35.84µ ± 1%
Dispatch_ours-16 98.46n ± 2%
Dispatch_telebot-16 270.9n ± 2%
Dispatch_gobot-16 246.1n ± 1%
LargeUnmarshal_ours-16 6.726µ ± 1%
LargeUnmarshal_gotba-16 8.066µ ± 1%
LargeUnmarshal_telebot-16 10.19µ ± 1%
LargeUnmarshal_gobot-16 8.231µ ± 1%
LargeUnmarshal_telego-16 7.849µ ± 2%
LargeUnmarshal_echotron-16 8.123µ ± 1%
Webhook_ours-16 1.832µ ± 4%
Webhook_gotba-16 2.082µ ± 0%
Webhook_telebot-16 2.194µ ± 1%
Webhook_gobot-16 2.082µ ± 1%
Webhook_telego-16 2.143µ ± 2%
Webhook_echotron-16 2.039µ ± 1%
geomean 4.658µ
│ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │
│ B/op │
Call_ours-16 11.17Ki ± 0%
Call_gotba-16 10.95Ki ± 0%
Call_telebot-16 13.16Ki ± 0%
Call_gobot-16 13.51Ki ± 0%
Call_telego-16 6.556Ki ± 0%
Call_ours-16 11.09Ki ± 1%
Call_gotba-16 10.97Ki ± 0%
Call_telebot-16 13.15Ki ± 0%
Call_gobot-16 13.50Ki ± 0%
Call_telego-16 6.547Ki ± 0%
Dispatch_ours-16 128.0 ± 0%
Dispatch_telebot-16 678.0 ± 0%
Dispatch_telebot-16 678.5 ± 0%
Dispatch_gobot-16 48.00 ± 0%
LargeUnmarshal_ours-16 5.881Ki ± 0%
LargeUnmarshal_ours-16 5.875Ki ± 0%
LargeUnmarshal_gotba-16 3.438Ki ± 0%
LargeUnmarshal_telebot-16 5.594Ki ± 0%
LargeUnmarshal_gobot-16 4.703Ki ± 0%
LargeUnmarshal_telego-16 6.621Ki ± 0%
LargeUnmarshal_telego-16 6.600Ki ± 0%
LargeUnmarshal_echotron-16 4.219Ki ± 0%
Webhook_ours-16 2.180Ki ± 0%
Webhook_gotba-16 1.461Ki ± 0%
Webhook_telebot-16 1.773Ki ± 0%
Webhook_gobot-16 1.789Ki ± 0%
Webhook_telego-16 3.060Ki ± 0%
Webhook_telego-16 3.058Ki ± 0%
Webhook_echotron-16 1.680Ki ± 0%
geomean 2.701Ki
geomean 2.699Ki
│ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │
│ allocs/op │
Call_ours-16 104.0 ± 0%
Call_ours-16 102.0 ± 0%
Call_gotba-16 125.0 ± 0%
Call_telebot-16 139.0 ± 0%
Call_gobot-16 176.0 ± 0%
@@ -72,4 +72,4 @@ Webhook_telebot-16
Webhook_gobot-16 16.00 ± 0%
Webhook_telego-16 11.00 ± 0%
Webhook_echotron-16 16.00 ± 0%
geomean 26.03
geomean 26.00
+201 -202
View File
@@ -2,206 +2,205 @@ goos: darwin
goarch: arm64
pkg: github.com/lukaszraczylo/go-telegram/test/benchmarks
cpu: Apple M4 Max
BenchmarkCall_ours-16 30754 38732 ns/op 11589 B/op 105 allocs/op
BenchmarkCall_ours-16 31054 39002 ns/op 11476 B/op 104 allocs/op
BenchmarkCall_ours-16 30990 38440 ns/op 11471 B/op 104 allocs/op
BenchmarkCall_ours-16 30301 39464 ns/op 11452 B/op 104 allocs/op
BenchmarkCall_ours-16 30438 38897 ns/op 11425 B/op 104 allocs/op
BenchmarkCall_ours-16 30783 39107 ns/op 11429 B/op 104 allocs/op
BenchmarkCall_ours-16 30486 39507 ns/op 11402 B/op 104 allocs/op
BenchmarkCall_ours-16 30045 37723 ns/op 11442 B/op 104 allocs/op
BenchmarkCall_ours-16 37867 33103 ns/op 11444 B/op 104 allocs/op
BenchmarkCall_ours-16 30522 39139 ns/op 11436 B/op 104 allocs/op
BenchmarkCall_gotba-16 29838 40947 ns/op 11243 B/op 125 allocs/op
BenchmarkCall_gotba-16 28545 41897 ns/op 11182 B/op 125 allocs/op
BenchmarkCall_gotba-16 28713 41146 ns/op 11197 B/op 125 allocs/op
BenchmarkCall_gotba-16 28480 42210 ns/op 11238 B/op 125 allocs/op
BenchmarkCall_gotba-16 28831 42004 ns/op 11204 B/op 125 allocs/op
BenchmarkCall_gotba-16 28484 42012 ns/op 11224 B/op 125 allocs/op
BenchmarkCall_gotba-16 30481 41283 ns/op 11212 B/op 125 allocs/op
BenchmarkCall_gotba-16 28646 42042 ns/op 11209 B/op 125 allocs/op
BenchmarkCall_gotba-16 28418 40680 ns/op 11250 B/op 125 allocs/op
BenchmarkCall_gotba-16 28358 42146 ns/op 11208 B/op 125 allocs/op
BenchmarkCall_telebot-16 27294 43739 ns/op 13522 B/op 139 allocs/op
BenchmarkCall_telebot-16 27763 43429 ns/op 13491 B/op 139 allocs/op
BenchmarkCall_telebot-16 27525 43618 ns/op 13478 B/op 139 allocs/op
BenchmarkCall_telebot-16 27423 43711 ns/op 13431 B/op 139 allocs/op
BenchmarkCall_telebot-16 27415 43704 ns/op 13473 B/op 139 allocs/op
BenchmarkCall_telebot-16 27268 43834 ns/op 13477 B/op 139 allocs/op
BenchmarkCall_telebot-16 28180 43488 ns/op 13486 B/op 139 allocs/op
BenchmarkCall_telebot-16 27480 43644 ns/op 13485 B/op 139 allocs/op
BenchmarkCall_telebot-16 27458 43581 ns/op 13479 B/op 139 allocs/op
BenchmarkCall_telebot-16 27949 43415 ns/op 13480 B/op 139 allocs/op
BenchmarkCall_gobot-16 19503 60924 ns/op 13847 B/op 176 allocs/op
BenchmarkCall_gobot-16 19620 61253 ns/op 13837 B/op 176 allocs/op
BenchmarkCall_gobot-16 19790 60869 ns/op 13839 B/op 176 allocs/op
BenchmarkCall_gobot-16 19574 61153 ns/op 13816 B/op 176 allocs/op
BenchmarkCall_gobot-16 19634 61070 ns/op 13830 B/op 176 allocs/op
BenchmarkCall_gobot-16 19569 61173 ns/op 13817 B/op 176 allocs/op
BenchmarkCall_gobot-16 19549 61688 ns/op 13851 B/op 176 allocs/op
BenchmarkCall_gobot-16 19918 60815 ns/op 13779 B/op 176 allocs/op
BenchmarkCall_gobot-16 19440 61667 ns/op 13830 B/op 176 allocs/op
BenchmarkCall_gobot-16 19660 61036 ns/op 13837 B/op 176 allocs/op
BenchmarkCall_telego-16 32732 36606 ns/op 6788 B/op 48 allocs/op
BenchmarkCall_telego-16 33837 35882 ns/op 6697 B/op 48 allocs/op
BenchmarkCall_telego-16 33074 36146 ns/op 6693 B/op 48 allocs/op
BenchmarkCall_telego-16 33400 36090 ns/op 6723 B/op 48 allocs/op
BenchmarkCall_telego-16 32684 36296 ns/op 6709 B/op 48 allocs/op
BenchmarkCall_telego-16 32926 36577 ns/op 6721 B/op 48 allocs/op
BenchmarkCall_telego-16 33928 35481 ns/op 6708 B/op 48 allocs/op
BenchmarkCall_telego-16 33195 36318 ns/op 6711 B/op 48 allocs/op
BenchmarkCall_telego-16 33140 36812 ns/op 6717 B/op 48 allocs/op
BenchmarkCall_telego-16 32343 37631 ns/op 6716 B/op 48 allocs/op
BenchmarkDispatch_ours-16 12199088 98.57 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12223122 97.66 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12088142 97.43 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12463010 100.9 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12284848 99.38 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11485198 101.1 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11685897 102.2 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11733669 102.1 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11811807 100.5 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11691974 103.1 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_telebot-16 4270724 284.6 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4369950 270.2 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4604205 269.9 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4431572 282.2 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4186550 272.0 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4472223 265.7 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4537406 265.0 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4544478 264.4 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4519431 266.7 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4448779 268.5 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_gobot-16 4493029 254.1 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4726080 261.5 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4884592 249.6 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4639986 256.7 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4424702 261.1 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4783779 249.2 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4916862 248.2 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4884650 249.9 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4822939 252.4 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4707606 250.5 ns/op 48 B/op 1 allocs/op
BenchmarkLargeUnmarshal_ours-16 177442 6697 ns/op 6024 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 178348 6700 ns/op 6021 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 180528 6686 ns/op 6022 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 182416 6659 ns/op 6021 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 179305 6675 ns/op 6022 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 176892 6724 ns/op 6021 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 185355 6392 ns/op 6022 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 189590 6390 ns/op 6022 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 189783 6438 ns/op 6022 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 187572 6483 ns/op 6022 B/op 34 allocs/op
BenchmarkLargeUnmarshal_gotba-16 142377 8296 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 144884 8274 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 146434 8295 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 145183 8260 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 144775 8416 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 139510 8224 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 146359 8542 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 144981 8346 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 146877 8399 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 145443 8454 ns/op 3519 B/op 56 allocs/op
BenchmarkLargeUnmarshal_telebot-16 116883 10327 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 114584 10333 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117277 10250 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117700 10270 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117328 10401 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 115922 10223 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117475 10049 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 123367 9866 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 123385 9811 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 122586 9873 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_gobot-16 148576 8064 ns/op 4817 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147967 8113 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 150718 7991 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 150271 8066 ns/op 4815 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147646 8066 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147889 8186 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 143164 8413 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 150464 8276 ns/op 4815 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 145249 8201 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 146100 8216 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_telego-16 149001 7802 ns/op 6781 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 152780 7835 ns/op 6775 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 156508 7817 ns/op 6780 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 154938 7816 ns/op 6777 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 150121 7809 ns/op 6778 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 152810 7791 ns/op 6780 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 159613 7784 ns/op 6778 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 154402 7729 ns/op 6780 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 157184 7660 ns/op 6782 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 155822 7768 ns/op 6780 B/op 31 allocs/op
BenchmarkLargeUnmarshal_echotron-16 144252 8147 ns/op 4323 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147961 8089 ns/op 4319 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149847 8049 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149128 8069 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147277 8075 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149132 8089 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 148837 8014 ns/op 4319 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149288 8050 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 146833 8097 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149233 8041 ns/op 4320 B/op 56 allocs/op
BenchmarkWebhook_ours-16 707102 1721 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 700047 1734 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 725071 1721 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 675003 1751 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 693903 1787 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 714036 1751 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 646494 1816 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 696355 1801 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 709545 1734 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 709700 1725 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_gotba-16 611132 2027 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 624046 2072 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 588478 2093 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 609744 2012 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 606912 2012 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 612211 2000 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 605488 2015 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 622398 2006 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 610478 2017 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 610350 2030 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 559515 2138 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 558510 2057 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 587257 2079 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 585321 2059 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 576236 2067 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 570172 2070 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 568270 2131 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 567567 2094 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 586588 2076 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 574495 2062 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_gobot-16 597710 2006 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 594742 2016 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 603187 1998 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 608301 2011 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 605532 1984 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 609892 1990 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 596637 1999 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 592108 1993 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 607069 1999 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 594915 1997 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_telego-16 603300 2021 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_telego-16 602503 2016 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_telego-16 618267 2016 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_telego-16 601855 2027 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_telego-16 612424 2035 ns/op 3132 B/op 11 allocs/op
BenchmarkWebhook_telego-16 595890 2074 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_telego-16 605565 2001 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_telego-16 610453 2096 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_telego-16 588056 2069 ns/op 3132 B/op 11 allocs/op
BenchmarkWebhook_telego-16 595764 2025 ns/op 3133 B/op 11 allocs/op
BenchmarkWebhook_echotron-16 599088 1975 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 626964 1975 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 635306 1967 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 606621 1965 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 625998 1965 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 646352 1976 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 634186 1971 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 628503 1982 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 612586 1975 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 622320 1965 ns/op 1720 B/op 16 allocs/op
BenchmarkCall_ours-16 27756 39971 ns/op 11497 B/op 103 allocs/op
BenchmarkCall_ours-16 30495 39111 ns/op 11329 B/op 102 allocs/op
BenchmarkCall_ours-16 29250 41427 ns/op 11356 B/op 102 allocs/op
BenchmarkCall_ours-16 29766 39680 ns/op 11366 B/op 102 allocs/op
BenchmarkCall_ours-16 29157 40066 ns/op 11338 B/op 102 allocs/op
BenchmarkCall_ours-16 30567 38404 ns/op 11276 B/op 102 allocs/op
BenchmarkCall_ours-16 30470 38923 ns/op 11306 B/op 102 allocs/op
BenchmarkCall_ours-16 30520 40212 ns/op 11364 B/op 102 allocs/op
BenchmarkCall_ours-16 30315 39595 ns/op 11361 B/op 102 allocs/op
BenchmarkCall_ours-16 28747 41549 ns/op 11434 B/op 102 allocs/op
BenchmarkCall_gotba-16 28140 43735 ns/op 11255 B/op 125 allocs/op
BenchmarkCall_gotba-16 27189 43528 ns/op 11247 B/op 125 allocs/op
BenchmarkCall_gotba-16 27940 43644 ns/op 11259 B/op 125 allocs/op
BenchmarkCall_gotba-16 29090 41643 ns/op 11232 B/op 125 allocs/op
BenchmarkCall_gotba-16 28002 42461 ns/op 11183 B/op 125 allocs/op
BenchmarkCall_gotba-16 28578 42082 ns/op 11204 B/op 125 allocs/op
BenchmarkCall_gotba-16 28549 41973 ns/op 11237 B/op 125 allocs/op
BenchmarkCall_gotba-16 29086 41702 ns/op 11203 B/op 125 allocs/op
BenchmarkCall_gotba-16 29630 41783 ns/op 11262 B/op 125 allocs/op
BenchmarkCall_gotba-16 28371 41810 ns/op 11217 B/op 125 allocs/op
BenchmarkCall_telebot-16 27510 43416 ns/op 13457 B/op 139 allocs/op
BenchmarkCall_telebot-16 28102 43319 ns/op 13473 B/op 139 allocs/op
BenchmarkCall_telebot-16 27558 43530 ns/op 13417 B/op 139 allocs/op
BenchmarkCall_telebot-16 27274 43654 ns/op 13445 B/op 139 allocs/op
BenchmarkCall_telebot-16 27627 43530 ns/op 13489 B/op 139 allocs/op
BenchmarkCall_telebot-16 27499 42836 ns/op 13467 B/op 139 allocs/op
BenchmarkCall_telebot-16 27860 43375 ns/op 13457 B/op 139 allocs/op
BenchmarkCall_telebot-16 27711 43400 ns/op 13439 B/op 139 allocs/op
BenchmarkCall_telebot-16 27668 43472 ns/op 13482 B/op 139 allocs/op
BenchmarkCall_telebot-16 28063 43182 ns/op 13487 B/op 139 allocs/op
BenchmarkCall_gobot-16 19645 60805 ns/op 13879 B/op 176 allocs/op
BenchmarkCall_gobot-16 19562 61374 ns/op 13823 B/op 176 allocs/op
BenchmarkCall_gobot-16 19575 60944 ns/op 13823 B/op 176 allocs/op
BenchmarkCall_gobot-16 19538 61461 ns/op 13844 B/op 176 allocs/op
BenchmarkCall_gobot-16 19624 61253 ns/op 13806 B/op 176 allocs/op
BenchmarkCall_gobot-16 19617 61127 ns/op 13824 B/op 176 allocs/op
BenchmarkCall_gobot-16 19516 61568 ns/op 13775 B/op 176 allocs/op
BenchmarkCall_gobot-16 19514 61340 ns/op 13828 B/op 176 allocs/op
BenchmarkCall_gobot-16 19392 60426 ns/op 13863 B/op 176 allocs/op
BenchmarkCall_gobot-16 23968 49951 ns/op 13844 B/op 176 allocs/op
BenchmarkCall_telego-16 33622 35493 ns/op 6780 B/op 48 allocs/op
BenchmarkCall_telego-16 33874 35438 ns/op 6703 B/op 48 allocs/op
BenchmarkCall_telego-16 34560 35482 ns/op 6704 B/op 48 allocs/op
BenchmarkCall_telego-16 33298 35830 ns/op 6711 B/op 48 allocs/op
BenchmarkCall_telego-16 33205 35946 ns/op 6706 B/op 48 allocs/op
BenchmarkCall_telego-16 33428 35949 ns/op 6707 B/op 48 allocs/op
BenchmarkCall_telego-16 33452 35974 ns/op 6692 B/op 48 allocs/op
BenchmarkCall_telego-16 33056 35853 ns/op 6705 B/op 48 allocs/op
BenchmarkCall_telego-16 33120 35808 ns/op 6703 B/op 48 allocs/op
BenchmarkCall_telego-16 33450 38996 ns/op 6699 B/op 48 allocs/op
BenchmarkDispatch_ours-16 12381547 96.05 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12636062 99.34 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12161170 98.43 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12205023 97.90 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12590581 98.83 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12033376 99.15 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12049588 98.48 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 12324108 98.38 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11924947 96.71 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11940064 99.26 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_telebot-16 4456072 262.5 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4330234 275.3 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4478779 268.7 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4394821 282.5 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4376773 271.6 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4516370 268.7 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4465942 276.0 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4399328 270.1 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4531597 268.3 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4376616 272.3 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_gobot-16 4911369 249.4 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4896456 246.3 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4789376 246.4 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4949206 247.7 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4902912 243.1 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4913300 244.7 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4925991 245.5 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4817457 245.9 ns/op 48 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4943328 245.8 ns/op 49 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4751266 248.1 ns/op 48 B/op 1 allocs/op
BenchmarkLargeUnmarshal_ours-16 180361 6682 ns/op 6019 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 178388 6765 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 182600 6701 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 178785 6710 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 181588 6726 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 177378 6730 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 181004 6729 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 176672 6682 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 184182 6726 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 179983 6813 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_gotba-16 138108 8579 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 148593 8143 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 147964 8075 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 147601 8161 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 148257 8020 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 150858 8058 ns/op 3519 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 149251 8040 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 151614 8054 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 152306 8050 ns/op 3519 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 152979 8094 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_telebot-16 119103 10113 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 115418 10247 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 116160 10260 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117031 10346 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 116731 10311 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 121227 10135 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 119989 10178 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 119311 10194 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 119388 10183 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 120195 10133 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_gobot-16 146700 8235 ns/op 4817 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147666 8230 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 148058 8212 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 148092 8210 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 146656 8208 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 148036 8259 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 146211 8287 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 146793 8279 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 146083 8232 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147385 8221 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_telego-16 150852 8155 ns/op 6762 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 152559 8040 ns/op 6758 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 146462 7989 ns/op 6757 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 154480 7842 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 155208 7811 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 149310 7848 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 156939 7835 ns/op 6757 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 154064 7867 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 153975 7849 ns/op 6758 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 152520 7811 ns/op 6758 B/op 31 allocs/op
BenchmarkLargeUnmarshal_echotron-16 145399 8167 ns/op 4323 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147351 8124 ns/op 4319 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 148053 8094 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147170 8124 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147792 8092 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 145971 8105 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 148708 8122 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147525 8105 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 148635 8165 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 150871 8124 ns/op 4320 B/op 56 allocs/op
BenchmarkWebhook_ours-16 705109 1911 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 651456 1911 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 683557 1875 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 668317 1830 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 703718 1833 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 707150 1844 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 700472 1810 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 699192 1828 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 671794 1828 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 687226 1826 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_gotba-16 544628 2080 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 565494 2081 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 579736 2081 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 581365 2082 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 580501 2075 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 582160 2091 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 563130 2089 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 573872 2088 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 569616 2093 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 599923 2069 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 562394 2162 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 562137 2272 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 488323 2220 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 538561 2199 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 581037 2187 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 540055 2213 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 546921 2203 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 574988 2188 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 588076 2154 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 571617 2181 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_gobot-16 588786 2080 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 551653 2080 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 590686 2084 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 588870 2096 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 606735 2064 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 597108 2084 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 600633 2069 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 589102 2110 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 583528 2104 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 599022 2073 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_telego-16 587408 2193 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 601533 2152 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 584689 2127 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 576732 2153 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 568095 2143 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 553896 2132 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 579055 2130 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 595776 2144 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 573843 2134 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 513824 2267 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_echotron-16 587734 2178 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 557188 2039 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 600524 2020 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 602959 2019 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 588694 2048 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 602366 2039 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 604621 2031 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 599146 2037 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 586056 2051 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 598456 2049 ns/op 1720 B/op 16 allocs/op
PASS
ok github.com/lukaszraczylo/go-telegram/test/benchmarks 241.963s
? github.com/lukaszraczylo/go-telegram/test/benchmarks/shared [no test files]
ok github.com/lukaszraczylo/go-telegram/test/benchmarks 242.858s