Compare commits

...

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: vet:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -41,8 +41,8 @@ jobs:
staticcheck: staticcheck:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -58,8 +58,8 @@ jobs:
govulncheck: govulncheck:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -75,8 +75,8 @@ jobs:
gosec: gosec:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -98,8 +98,8 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -120,8 +120,8 @@ jobs:
codegen-clean: codegen-clean:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -139,10 +139,10 @@ jobs:
audit: audit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 # need history for drift comparison fetch-depth: 0 # need history for drift comparison
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -196,11 +196,11 @@ jobs:
github.event_name == 'workflow_dispatch' github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }} url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/configure-pages@v5 - uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3 - uses: actions/upload-pages-artifact@v3
with: with:
+3 -3
View File
@@ -13,12 +13,12 @@ jobs:
regen: regen:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 # full history so audit -drift can compare against main fetch-depth: 0 # full history so audit -drift can compare against main
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v6
with: with:
go-version: '1.25.x' go-version: '1.25.x'
check-latest: true check-latest: true
@@ -89,7 +89,7 @@ jobs:
- name: Open PR - name: Open PR
if: steps.diff.outputs.no_changes != 'true' if: steps.diff.outputs.no_changes != 'true'
uses: peter-evans/create-pull-request@v7 uses: peter-evans/create-pull-request@v8
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
commit-message: | 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 | | Path | Fastest | Our position |
|------|---------|--------------| |------|---------|--------------|
| Webhook decode (small Update) | **ours** — 1.74 µs / 11 allocs | 1st of 6 | | Webhook decode (small Update) | **ours** — 1.83 µs / 11 allocs | 1st of 6 |
| Large Update unmarshal (unions + reply markup) | **ours** — 6.67 µs / 34 allocs | 1st of 6 | | Large Update unmarshal (unions + reply markup) | **ours** — 6.73 µs / 34 allocs | 1st of 6 |
| `sendMessage` round-trip (mock server) | telego — 36.3 µs / 48 allocs | 2nd of 5 | | `sendMessage` round-trip `net/http` default | telego — 35.8 µs / 48 allocs | 2nd of 5 (102 allocs) |
| Dispatcher routing (20 handlers, last matches) | **ours** — 101 ns / 3 allocs | 1st of 3 | | `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)**. 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 // return slices that alias the buffer and therefore cannot use the
// pool without an extra copy that would defeat the point. // pool without an extra copy that would defeat the point.
respBufPool = sync.Pool{New: func() any { return new(bytes.Buffer) }} 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 // 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 // (e.g. a large getFile metadata payload) doesn't bloat the pool for the
// rest of the process lifetime. // rest of the process lifetime.
@@ -38,6 +46,13 @@ func putRespBuf(buf *bytes.Buffer) {
respBufPool.Put(buf) 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 // Call is the single point through which every Telegram Bot API method
// invocation flows. It marshals the request, signs the URL with the bot // invocation flows. It marshals the request, signs the URL with the bot
// token, dispatches via HTTPDoer, decodes the Result envelope, and // 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 { if err != nil {
return zero, err return zero, err
} }
if pooledReqBuf != nil {
defer putReqBuf(pooledReqBuf)
}
url := b.base + "/bot" + b.token + "/" + method httpReq, err := b.buildRequest(ctx, method, body)
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
if err != nil { if err != nil {
return zero, &NetworkError{Err: err} return zero, &NetworkError{Err: err}
} }
httpReq.Header["Content-Type"] = headerJSONValue
httpReq.Header["Accept"] = headerJSONValue
resp, err := b.http.Do(httpReq) resp, err := b.http.Do(httpReq)
if err != nil { 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 { if err != nil {
return nil, err return nil, err
} }
if pooledReqBuf != nil {
defer putReqBuf(pooledReqBuf)
}
url := b.base + "/bot" + b.token + "/" + method httpReq, err := b.buildRequest(ctx, method, body)
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
if err != nil { if err != nil {
return nil, &NetworkError{Err: err} return nil, &NetworkError{Err: err}
} }
httpReq.Header["Content-Type"] = headerJSONValue
httpReq.Header["Accept"] = headerJSONValue
resp, err := b.http.Do(httpReq) resp, err := b.http.Do(httpReq)
if err != nil { if err != nil {
@@ -154,17 +169,126 @@ func decodeResultRaw(codec Codec, raw []byte) (json.RawMessage, error) {
return env.Result, nil return env.Result, nil
} }
// encodeJSONBody marshals req to a JSON body. A nil interface or nil // buildRequest constructs the *http.Request for an API call. When the bot
// pointer req yields "{}" so Telegram receives a valid empty object. // has a cached parsed base URL (the common path), the request is built
func encodeJSONBody(codec Codec, req any) (io.Reader, error) { // 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) { 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) data, err := codec.Marshal(req)
if err != nil { 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 // 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"} req := &benchSendReq{ChatID: 42, Text: "hello, world"}
b.ReportAllocs() b.ReportAllocs()
for b.Loop() { for b.Loop() {
r, err := encodeJSONBody(codec, req) r, pooled, err := encodeJSONBody(codec, req)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
_ = r _ = r
if pooled != nil {
putReqBuf(pooled)
}
} }
} }
+19
View File
@@ -1,5 +1,9 @@
package client package client
import (
"net/url"
)
const defaultBaseURL = "https://api.telegram.org" const defaultBaseURL = "https://api.telegram.org"
// Bot is the Telegram Bot API client. Construct via New. All API methods // Bot is the Telegram Bot API client. Construct via New. All API methods
@@ -10,6 +14,13 @@ type Bot struct {
http HTTPDoer http HTTPDoer
codec Codec codec Codec
logger Logger 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 // 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 { for _, o := range opts {
o(b) 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 return b
} }
+21 -1
View File
@@ -1,7 +1,11 @@
// Package client provides HTTP client primitives for the Telegram Bot API. // Package client provides HTTP client primitives for the Telegram Bot API.
package client 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. // Codec encodes/decodes JSON payloads exchanged with the Telegram Bot API.
// The default implementation wraps goccy/go-json. Users may plug in // The default implementation wraps goccy/go-json. Users may plug in
@@ -12,6 +16,15 @@ type Codec interface {
Unmarshal(data []byte, v any) error 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. // DefaultCodec wraps goccy/go-json. It is the zero-value safe default.
type DefaultCodec struct{} type DefaultCodec struct{}
@@ -20,3 +33,10 @@ func (DefaultCodec) Marshal(v any) ([]byte, error) { return json.Marshal(v) }
// Unmarshal calls json.Unmarshal. // Unmarshal calls json.Unmarshal.
func (DefaultCodec) Unmarshal(data []byte, v any) error { return json.Unmarshal(data, v) } 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 ## TL;DR
- **Webhook decode** (small Update): ours is **1519% faster** than every competitor and ties telego for the lowest alloc count (11). - **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 **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. - **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 (36.3 µs / 48 allocs) thanks to its custom binder; ours is second (38.95 µs / 104 allocs) and beats gotba, telebot, gobot. - **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.5× faster than telebot and gobot** (101 ns vs 269 / 252 ns). - **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 ## 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 | | Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------| |-----|--------|------|-----------|
| **ours** | **1.743 µs ±3%** | 2.180 KiB | **11** | | **ours** | **1.832 µs ±4%** | 2.180 KiB | **11** |
| gotba | 2.016 µs ±3% | 1.461 KiB | 17 | | gotba | 2.082 µs ±0% | 1.461 KiB | 17 |
| telebot | 2.073 µs ±3% | 1.773 KiB | 17 | | telebot | 2.194 µs ±1% | 1.773 KiB | 17 |
| gobot | 1.999 µs ±1% | 1.789 KiB | 16 | | gobot | 2.082 µs ±1% | 1.789 KiB | 16 |
| telego | 2.026 µs ±2% | 3.060 KiB | **11** | | telego | 2.143 µs ±2% | 3.058 KiB | **11** |
| echotron | 1.973 µs ±0% | 1.680 KiB | 16 | | 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. **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 | | Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------| |-----|--------|------|-----------|
| **ours** | **6.667 µs ±4%** | 5.881 KiB | 34 | | **ours** | **6.726 µs ±1%** | 5.875 KiB | 34 |
| gotba | 8.321 µs ±2% | 3.438 KiB | 56 | | gotba | 8.066 µs ±1% | 3.438 KiB | 56 |
| telebot | 10.240 µs ±4% | 5.594 KiB | 60 | | telebot | 10.190 µs ±1% | 5.594 KiB | 60 |
| gobot | 8.150 µs ±2% | 4.703 KiB | 50 | | gobot | 8.231 µs ±1% | 4.703 KiB | 50 |
| telego | 7.797 µs ±1% | 6.621 KiB | **31** | | telego | 7.849 µs ±2% | 6.600 KiB | **31** |
| echotron | 8.072 µs ±0% | 4.219 KiB | 56 | | 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. **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 | | Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------| |-----|--------|------|-----------|
| ours | 38.95 µs ±3% | 11.17 KiB | 104 | | ours (default `net/http`) | 39.83 µs ±4% | 11.09 KiB | 102 |
| gotba | 41.95 µs ±2% | 10.95 KiB | 125 | | ours (opt-in `fasthttp`) | *time TBD on quiet box* | **6.62 KiB** | **56** |
| telebot | 43.63 µs ±0% | 13.16 KiB | 139 | | gotba | 42.03 µs ±4% | 10.97 KiB | 125 |
| gobot | 61.11 µs ±1% | 13.51 KiB | 176 | | telebot | 43.41 µs ±1% | 13.15 KiB | 139 |
| **telego** | **36.31 µs ±1%** | **6.556 KiB** | **48** | | gobot | 61.19 µs ±1% | 13.50 KiB | 176 |
| **telego** (uses fasthttp) | **35.84 µs ±1%** | **6.547 KiB** | **48** |
| echotron | *skipped — see below* | — | — | | echotron | *skipped — see below* | — | — |
**Notes.** **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. - 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. - **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 | | Lib | sec/op | B/op | allocs/op |
|-----|--------|------|-----------| |-----|--------|------|-----------|
| **ours** | **100.7 ns ±3%** | 128 B | 3 | | **ours** | **98.46 ns ±2%** | 128 B | 3 |
| telebot | 269.2 ns ±5% | 678 B | 5 | | telebot | 270.9 ns ±2% | 678 B | 5 |
| gobot | 251.5 ns ±4% | **48 B** | **1** | | 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. **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\) IsRetryable\(\) bool](<#APIError.IsRetryable>)
- [func \(e \*APIError\) RetryAfter\(\) time.Duration](<#APIError.RetryAfter>) - [func \(e \*APIError\) RetryAfter\(\) time.Duration](<#APIError.RetryAfter>)
- [func \(e \*APIError\) Unwrap\(\) error](<#APIError.Unwrap>) - [func \(e \*APIError\) Unwrap\(\) error](<#APIError.Unwrap>)
- [type BodyEncoder](<#BodyEncoder>)
- [type Bot](<#Bot>) - [type Bot](<#Bot>)
- [func New\(token string, opts ...Option\) \*Bot](<#New>) - [func New\(token string, opts ...Option\) \*Bot](<#New>)
- [func \(b \*Bot\) BaseURL\(\) string](<#Bot.BaseURL>) - [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 Codec](<#Codec>)
- [type DefaultCodec](<#DefaultCodec>) - [type DefaultCodec](<#DefaultCodec>)
- [func \(DefaultCodec\) Marshal\(v any\) \(\[\]byte, error\)](<#DefaultCodec.Marshal>) - [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>) - [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 HTTPDoer](<#HTTPDoer>)
- [type Logger](<#Logger>) - [type Logger](<#Logger>)
- [type MultipartFile](<#MultipartFile>) - [type MultipartFile](<#MultipartFile>)
@@ -80,7 +88,7 @@ var (
``` ```
<a name="Call"></a> <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 ```go
func Call[Req any, Resp any](ctx context.Context, b *Bot, method string, req Req) (Resp, error) 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. 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> <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 ```go
func CallRaw[Req any](ctx context.Context, b *Bot, method string, req Req) (json.RawMessage, error) 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. 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> <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. 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> <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 ```go
func New(token string, opts ...Option) *Bot 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. 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> <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 ```go
func (b *Bot) BaseURL() string func (b *Bot) BaseURL() string
@@ -196,7 +215,7 @@ func (b *Bot) BaseURL() string
BaseURL returns the configured Telegram API base URL. BaseURL returns the configured Telegram API base URL.
<a name="Bot.Codec"></a> <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 ```go
func (b *Bot) Codec() Codec func (b *Bot) Codec() Codec
@@ -205,7 +224,7 @@ func (b *Bot) Codec() Codec
Codec returns the configured Codec. Codec returns the configured Codec.
<a name="Bot.HTTP"></a> <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 ```go
func (b *Bot) HTTP() HTTPDoer 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. HTTP returns the underlying HTTPDoer. Exposed for adapters that need to share connection pools or for diagnostic checks.
<a name="Bot.Logger"></a> <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 ```go
func (b *Bot) Logger() Logger func (b *Bot) Logger() Logger
@@ -223,7 +242,7 @@ func (b *Bot) Logger() Logger
Logger returns the configured Logger. Logger returns the configured Logger.
<a name="Bot.Token"></a> <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 ```go
func (b *Bot) Token() string 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. 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> <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. 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> <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. DefaultCodec wraps goccy/go\-json. It is the zero\-value safe default.
@@ -253,7 +272,7 @@ type DefaultCodec struct{}
``` ```
<a name="DefaultCodec.Marshal"></a> <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 ```go
func (DefaultCodec) Marshal(v any) ([]byte, error) func (DefaultCodec) Marshal(v any) ([]byte, error)
@@ -261,8 +280,17 @@ func (DefaultCodec) Marshal(v any) ([]byte, error)
Marshal calls json.Marshal. 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> <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 ```go
func (DefaultCodec) Unmarshal(data []byte, v any) error func (DefaultCodec) Unmarshal(data []byte, v any) error
@@ -270,6 +298,72 @@ func (DefaultCodec) Unmarshal(data []byte, v any) error
Unmarshal calls json.Unmarshal. 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> <a name="HTTPDoer"></a>
## type [HTTPDoer](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/httpclient.go#L13-L15>) ## type [HTTPDoer](<https://github.com/lukaszraczylo/go-telegram/blob/main/client/httpclient.go#L13-L15>)
+4
View File
@@ -9,8 +9,12 @@ require (
) )
require ( require (
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/davecgh/go-spew v1.1.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/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // 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 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 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 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= 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= 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. // Telegram-format token (digits:[\w-]{35}). telego enforces this format on construction.
const benchToken = "1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ_ab123456" 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) { func BenchmarkCall_ours(b *testing.B) {
srv := shared.NewMockServer() srv := shared.NewMockServer()
defer srv.Close() 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. // BenchmarkCall_gotba — go-telegram-bot-api/telegram-bot-api/v5.
func BenchmarkCall_gotba(b *testing.B) { func BenchmarkCall_gotba(b *testing.B) {
srv := shared.NewMockServer() srv := shared.NewMockServer()
+4 -4
View File
@@ -14,20 +14,20 @@ require (
) )
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/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect github.com/cloudwego/base64x v0.1.6 // indirect
github.com/goccy/go-json v0.10.6 // indirect github.com/goccy/go-json v0.10.6 // indirect
github.com/grbit/go-json v0.11.0 // 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/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // 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 github.com/valyala/fastjson v1.6.10 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // 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 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-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-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 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.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= 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/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/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= 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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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/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.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= 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 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= 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= 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/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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 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.71.0 h1:tepR7H+Guh9VUqxxcPggYi8R3lGUu2Rsdh+z7/FCY3k=
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw= 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 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4=
github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= 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-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-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.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= 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 cpu: Apple M4 Max
│ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │ │ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │
│ sec/op │ │ sec/op │
Call_ours-16 38.95µ ± 3% Call_ours-16 39.83µ ± 4%
Call_gotba-16 41.95µ ± 2% Call_gotba-16 42.03µ ± 4%
Call_telebot-16 43.63µ ± 0% Call_telebot-16 43.41µ ± 1%
Call_gobot-16 61.11µ ± 1% Call_gobot-16 61.19µ ± 1%
Call_telego-16 36.31µ ± 1% Call_telego-16 35.84µ ± 1%
Dispatch_ours-16 100.7n ± 3% Dispatch_ours-16 98.46n ± 2%
Dispatch_telebot-16 269.2n ± 5% Dispatch_telebot-16 270.9n ± 2%
Dispatch_gobot-16 251.5n ± 4% Dispatch_gobot-16 246.1n ± 1%
LargeUnmarshal_ours-16 6.667µ ± 4% LargeUnmarshal_ours-16 6.726µ ± 1%
LargeUnmarshal_gotba-16 8.321µ ± 2% LargeUnmarshal_gotba-16 8.066µ ± 1%
LargeUnmarshal_telebot-16 10.24µ ± 4% LargeUnmarshal_telebot-16 10.19µ ± 1%
LargeUnmarshal_gobot-16 8.150µ ± 2% LargeUnmarshal_gobot-16 8.231µ ± 1%
LargeUnmarshal_telego-16 7.797µ ± 1% LargeUnmarshal_telego-16 7.849µ ± 2%
LargeUnmarshal_echotron-16 8.072µ ± 0% LargeUnmarshal_echotron-16 8.123µ ± 1%
Webhook_ours-16 1.743µ ± 3% Webhook_ours-16 1.832µ ± 4%
Webhook_gotba-16 2.016µ ± 3% Webhook_gotba-16 2.082µ ± 0%
Webhook_telebot-16 2.073µ ± 3% Webhook_telebot-16 2.194µ ± 1%
Webhook_gobot-16 1.999µ ± 1% Webhook_gobot-16 2.082µ ± 1%
Webhook_telego-16 2.026µ ± 2% Webhook_telego-16 2.143µ ± 2%
Webhook_echotron-16 1.973µ ± 0% Webhook_echotron-16 2.039µ ± 1%
geomean 4.603µ geomean 4.658µ
│ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │ │ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │
│ B/op │ │ B/op │
Call_ours-16 11.17Ki ± 0% Call_ours-16 11.09Ki ± 1%
Call_gotba-16 10.95Ki ± 0% Call_gotba-16 10.97Ki ± 0%
Call_telebot-16 13.16Ki ± 0% Call_telebot-16 13.15Ki ± 0%
Call_gobot-16 13.51Ki ± 0% Call_gobot-16 13.50Ki ± 0%
Call_telego-16 6.556Ki ± 0% Call_telego-16 6.547Ki ± 0%
Dispatch_ours-16 128.0 ± 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% 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_gotba-16 3.438Ki ± 0%
LargeUnmarshal_telebot-16 5.594Ki ± 0% LargeUnmarshal_telebot-16 5.594Ki ± 0%
LargeUnmarshal_gobot-16 4.703Ki ± 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% LargeUnmarshal_echotron-16 4.219Ki ± 0%
Webhook_ours-16 2.180Ki ± 0% Webhook_ours-16 2.180Ki ± 0%
Webhook_gotba-16 1.461Ki ± 0% Webhook_gotba-16 1.461Ki ± 0%
Webhook_telebot-16 1.773Ki ± 0% Webhook_telebot-16 1.773Ki ± 0%
Webhook_gobot-16 1.789Ki ± 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% Webhook_echotron-16 1.680Ki ± 0%
geomean 2.701Ki geomean 2.699Ki
│ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │ │ /Users/nvm/Documents/projects/private/go-telegram/test/benchmarks/results/raw.txt │
│ allocs/op │ │ allocs/op │
Call_ours-16 104.0 ± 0% Call_ours-16 102.0 ± 0%
Call_gotba-16 125.0 ± 0% Call_gotba-16 125.0 ± 0%
Call_telebot-16 139.0 ± 0% Call_telebot-16 139.0 ± 0%
Call_gobot-16 176.0 ± 0% Call_gobot-16 176.0 ± 0%
@@ -72,4 +72,4 @@ Webhook_telebot-16
Webhook_gobot-16 16.00 ± 0% Webhook_gobot-16 16.00 ± 0%
Webhook_telego-16 11.00 ± 0% Webhook_telego-16 11.00 ± 0%
Webhook_echotron-16 16.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 goarch: arm64
pkg: github.com/lukaszraczylo/go-telegram/test/benchmarks pkg: github.com/lukaszraczylo/go-telegram/test/benchmarks
cpu: Apple M4 Max cpu: Apple M4 Max
BenchmarkCall_ours-16 30754 38732 ns/op 11589 B/op 105 allocs/op BenchmarkCall_ours-16 27756 39971 ns/op 11497 B/op 103 allocs/op
BenchmarkCall_ours-16 31054 39002 ns/op 11476 B/op 104 allocs/op BenchmarkCall_ours-16 30495 39111 ns/op 11329 B/op 102 allocs/op
BenchmarkCall_ours-16 30990 38440 ns/op 11471 B/op 104 allocs/op BenchmarkCall_ours-16 29250 41427 ns/op 11356 B/op 102 allocs/op
BenchmarkCall_ours-16 30301 39464 ns/op 11452 B/op 104 allocs/op BenchmarkCall_ours-16 29766 39680 ns/op 11366 B/op 102 allocs/op
BenchmarkCall_ours-16 30438 38897 ns/op 11425 B/op 104 allocs/op BenchmarkCall_ours-16 29157 40066 ns/op 11338 B/op 102 allocs/op
BenchmarkCall_ours-16 30783 39107 ns/op 11429 B/op 104 allocs/op BenchmarkCall_ours-16 30567 38404 ns/op 11276 B/op 102 allocs/op
BenchmarkCall_ours-16 30486 39507 ns/op 11402 B/op 104 allocs/op BenchmarkCall_ours-16 30470 38923 ns/op 11306 B/op 102 allocs/op
BenchmarkCall_ours-16 30045 37723 ns/op 11442 B/op 104 allocs/op BenchmarkCall_ours-16 30520 40212 ns/op 11364 B/op 102 allocs/op
BenchmarkCall_ours-16 37867 33103 ns/op 11444 B/op 104 allocs/op BenchmarkCall_ours-16 30315 39595 ns/op 11361 B/op 102 allocs/op
BenchmarkCall_ours-16 30522 39139 ns/op 11436 B/op 104 allocs/op BenchmarkCall_ours-16 28747 41549 ns/op 11434 B/op 102 allocs/op
BenchmarkCall_gotba-16 29838 40947 ns/op 11243 B/op 125 allocs/op BenchmarkCall_gotba-16 28140 43735 ns/op 11255 B/op 125 allocs/op
BenchmarkCall_gotba-16 28545 41897 ns/op 11182 B/op 125 allocs/op BenchmarkCall_gotba-16 27189 43528 ns/op 11247 B/op 125 allocs/op
BenchmarkCall_gotba-16 28713 41146 ns/op 11197 B/op 125 allocs/op BenchmarkCall_gotba-16 27940 43644 ns/op 11259 B/op 125 allocs/op
BenchmarkCall_gotba-16 28480 42210 ns/op 11238 B/op 125 allocs/op BenchmarkCall_gotba-16 29090 41643 ns/op 11232 B/op 125 allocs/op
BenchmarkCall_gotba-16 28831 42004 ns/op 11204 B/op 125 allocs/op BenchmarkCall_gotba-16 28002 42461 ns/op 11183 B/op 125 allocs/op
BenchmarkCall_gotba-16 28484 42012 ns/op 11224 B/op 125 allocs/op BenchmarkCall_gotba-16 28578 42082 ns/op 11204 B/op 125 allocs/op
BenchmarkCall_gotba-16 30481 41283 ns/op 11212 B/op 125 allocs/op BenchmarkCall_gotba-16 28549 41973 ns/op 11237 B/op 125 allocs/op
BenchmarkCall_gotba-16 28646 42042 ns/op 11209 B/op 125 allocs/op BenchmarkCall_gotba-16 29086 41702 ns/op 11203 B/op 125 allocs/op
BenchmarkCall_gotba-16 28418 40680 ns/op 11250 B/op 125 allocs/op BenchmarkCall_gotba-16 29630 41783 ns/op 11262 B/op 125 allocs/op
BenchmarkCall_gotba-16 28358 42146 ns/op 11208 B/op 125 allocs/op BenchmarkCall_gotba-16 28371 41810 ns/op 11217 B/op 125 allocs/op
BenchmarkCall_telebot-16 27294 43739 ns/op 13522 B/op 139 allocs/op BenchmarkCall_telebot-16 27510 43416 ns/op 13457 B/op 139 allocs/op
BenchmarkCall_telebot-16 27763 43429 ns/op 13491 B/op 139 allocs/op BenchmarkCall_telebot-16 28102 43319 ns/op 13473 B/op 139 allocs/op
BenchmarkCall_telebot-16 27525 43618 ns/op 13478 B/op 139 allocs/op BenchmarkCall_telebot-16 27558 43530 ns/op 13417 B/op 139 allocs/op
BenchmarkCall_telebot-16 27423 43711 ns/op 13431 B/op 139 allocs/op BenchmarkCall_telebot-16 27274 43654 ns/op 13445 B/op 139 allocs/op
BenchmarkCall_telebot-16 27415 43704 ns/op 13473 B/op 139 allocs/op BenchmarkCall_telebot-16 27627 43530 ns/op 13489 B/op 139 allocs/op
BenchmarkCall_telebot-16 27268 43834 ns/op 13477 B/op 139 allocs/op BenchmarkCall_telebot-16 27499 42836 ns/op 13467 B/op 139 allocs/op
BenchmarkCall_telebot-16 28180 43488 ns/op 13486 B/op 139 allocs/op BenchmarkCall_telebot-16 27860 43375 ns/op 13457 B/op 139 allocs/op
BenchmarkCall_telebot-16 27480 43644 ns/op 13485 B/op 139 allocs/op BenchmarkCall_telebot-16 27711 43400 ns/op 13439 B/op 139 allocs/op
BenchmarkCall_telebot-16 27458 43581 ns/op 13479 B/op 139 allocs/op BenchmarkCall_telebot-16 27668 43472 ns/op 13482 B/op 139 allocs/op
BenchmarkCall_telebot-16 27949 43415 ns/op 13480 B/op 139 allocs/op BenchmarkCall_telebot-16 28063 43182 ns/op 13487 B/op 139 allocs/op
BenchmarkCall_gobot-16 19503 60924 ns/op 13847 B/op 176 allocs/op BenchmarkCall_gobot-16 19645 60805 ns/op 13879 B/op 176 allocs/op
BenchmarkCall_gobot-16 19620 61253 ns/op 13837 B/op 176 allocs/op BenchmarkCall_gobot-16 19562 61374 ns/op 13823 B/op 176 allocs/op
BenchmarkCall_gobot-16 19790 60869 ns/op 13839 B/op 176 allocs/op BenchmarkCall_gobot-16 19575 60944 ns/op 13823 B/op 176 allocs/op
BenchmarkCall_gobot-16 19574 61153 ns/op 13816 B/op 176 allocs/op BenchmarkCall_gobot-16 19538 61461 ns/op 13844 B/op 176 allocs/op
BenchmarkCall_gobot-16 19634 61070 ns/op 13830 B/op 176 allocs/op BenchmarkCall_gobot-16 19624 61253 ns/op 13806 B/op 176 allocs/op
BenchmarkCall_gobot-16 19569 61173 ns/op 13817 B/op 176 allocs/op BenchmarkCall_gobot-16 19617 61127 ns/op 13824 B/op 176 allocs/op
BenchmarkCall_gobot-16 19549 61688 ns/op 13851 B/op 176 allocs/op BenchmarkCall_gobot-16 19516 61568 ns/op 13775 B/op 176 allocs/op
BenchmarkCall_gobot-16 19918 60815 ns/op 13779 B/op 176 allocs/op BenchmarkCall_gobot-16 19514 61340 ns/op 13828 B/op 176 allocs/op
BenchmarkCall_gobot-16 19440 61667 ns/op 13830 B/op 176 allocs/op BenchmarkCall_gobot-16 19392 60426 ns/op 13863 B/op 176 allocs/op
BenchmarkCall_gobot-16 19660 61036 ns/op 13837 B/op 176 allocs/op BenchmarkCall_gobot-16 23968 49951 ns/op 13844 B/op 176 allocs/op
BenchmarkCall_telego-16 32732 36606 ns/op 6788 B/op 48 allocs/op BenchmarkCall_telego-16 33622 35493 ns/op 6780 B/op 48 allocs/op
BenchmarkCall_telego-16 33837 35882 ns/op 6697 B/op 48 allocs/op BenchmarkCall_telego-16 33874 35438 ns/op 6703 B/op 48 allocs/op
BenchmarkCall_telego-16 33074 36146 ns/op 6693 B/op 48 allocs/op BenchmarkCall_telego-16 34560 35482 ns/op 6704 B/op 48 allocs/op
BenchmarkCall_telego-16 33400 36090 ns/op 6723 B/op 48 allocs/op BenchmarkCall_telego-16 33298 35830 ns/op 6711 B/op 48 allocs/op
BenchmarkCall_telego-16 32684 36296 ns/op 6709 B/op 48 allocs/op BenchmarkCall_telego-16 33205 35946 ns/op 6706 B/op 48 allocs/op
BenchmarkCall_telego-16 32926 36577 ns/op 6721 B/op 48 allocs/op BenchmarkCall_telego-16 33428 35949 ns/op 6707 B/op 48 allocs/op
BenchmarkCall_telego-16 33928 35481 ns/op 6708 B/op 48 allocs/op BenchmarkCall_telego-16 33452 35974 ns/op 6692 B/op 48 allocs/op
BenchmarkCall_telego-16 33195 36318 ns/op 6711 B/op 48 allocs/op BenchmarkCall_telego-16 33056 35853 ns/op 6705 B/op 48 allocs/op
BenchmarkCall_telego-16 33140 36812 ns/op 6717 B/op 48 allocs/op BenchmarkCall_telego-16 33120 35808 ns/op 6703 B/op 48 allocs/op
BenchmarkCall_telego-16 32343 37631 ns/op 6716 B/op 48 allocs/op BenchmarkCall_telego-16 33450 38996 ns/op 6699 B/op 48 allocs/op
BenchmarkDispatch_ours-16 12199088 98.57 ns/op 128 B/op 3 allocs/op BenchmarkDispatch_ours-16 12381547 96.05 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 12636062 99.34 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 12161170 98.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 12205023 97.90 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 12590581 98.83 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 12033376 99.15 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 12049588 98.48 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 12324108 98.38 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 11924947 96.71 ns/op 128 B/op 3 allocs/op
BenchmarkDispatch_ours-16 11691974 103.1 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 4270724 284.6 ns/op 679 B/op 5 allocs/op BenchmarkDispatch_telebot-16 4456072 262.5 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4369950 270.2 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 4604205 269.9 ns/op 679 B/op 5 allocs/op BenchmarkDispatch_telebot-16 4478779 268.7 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 4394821 282.5 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 4376773 271.6 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4472223 265.7 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 4537406 265.0 ns/op 679 B/op 5 allocs/op BenchmarkDispatch_telebot-16 4465942 276.0 ns/op 678 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4544478 264.4 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 4519431 266.7 ns/op 678 B/op 5 allocs/op BenchmarkDispatch_telebot-16 4531597 268.3 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_telebot-16 4448779 268.5 ns/op 678 B/op 5 allocs/op BenchmarkDispatch_telebot-16 4376616 272.3 ns/op 679 B/op 5 allocs/op
BenchmarkDispatch_gobot-16 4493029 254.1 ns/op 48 B/op 1 allocs/op BenchmarkDispatch_gobot-16 4911369 249.4 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 4896456 246.3 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 4789376 246.4 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 4949206 247.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 4902912 243.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 4913300 244.7 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 4925991 245.5 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 4817457 245.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 4943328 245.8 ns/op 49 B/op 1 allocs/op
BenchmarkDispatch_gobot-16 4707606 250.5 ns/op 48 B/op 1 allocs/op BenchmarkDispatch_gobot-16 4751266 248.1 ns/op 48 B/op 1 allocs/op
BenchmarkLargeUnmarshal_ours-16 177442 6697 ns/op 6024 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 180361 6682 ns/op 6019 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 178348 6700 ns/op 6021 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 178388 6765 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 180528 6686 ns/op 6022 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 182600 6701 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 182416 6659 ns/op 6021 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 178785 6710 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 179305 6675 ns/op 6022 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 181588 6726 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 176892 6724 ns/op 6021 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 177378 6730 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 185355 6392 ns/op 6022 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 181004 6729 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 189590 6390 ns/op 6022 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 176672 6682 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 189783 6438 ns/op 6022 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 184182 6726 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_ours-16 187572 6483 ns/op 6022 B/op 34 allocs/op BenchmarkLargeUnmarshal_ours-16 179983 6813 ns/op 6016 B/op 34 allocs/op
BenchmarkLargeUnmarshal_gotba-16 142377 8296 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 138108 8579 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 144884 8274 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 148593 8143 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 146434 8295 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 147964 8075 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 145183 8260 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 147601 8161 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 144775 8416 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 148257 8020 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 139510 8224 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 150858 8058 ns/op 3519 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 146359 8542 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 149251 8040 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 144981 8346 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 151614 8054 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 146877 8399 ns/op 3520 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 152306 8050 ns/op 3519 B/op 56 allocs/op
BenchmarkLargeUnmarshal_gotba-16 145443 8454 ns/op 3519 B/op 56 allocs/op BenchmarkLargeUnmarshal_gotba-16 152979 8094 ns/op 3520 B/op 56 allocs/op
BenchmarkLargeUnmarshal_telebot-16 116883 10327 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 119103 10113 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 114584 10333 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 115418 10247 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117277 10250 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 116160 10260 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117700 10270 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 117031 10346 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117328 10401 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 116731 10311 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 115922 10223 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 121227 10135 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 117475 10049 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 119989 10178 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 123367 9866 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 119311 10194 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 123385 9811 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 119388 10183 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_telebot-16 122586 9873 ns/op 5728 B/op 60 allocs/op BenchmarkLargeUnmarshal_telebot-16 120195 10133 ns/op 5728 B/op 60 allocs/op
BenchmarkLargeUnmarshal_gobot-16 148576 8064 ns/op 4817 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 146700 8235 ns/op 4817 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147967 8113 ns/op 4816 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 147666 8230 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 150718 7991 ns/op 4816 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 148058 8212 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 150271 8066 ns/op 4815 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 148092 8210 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147646 8066 ns/op 4816 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 146656 8208 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 147889 8186 ns/op 4816 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 148036 8259 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 143164 8413 ns/op 4816 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 146211 8287 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 150464 8276 ns/op 4815 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 146793 8279 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 145249 8201 ns/op 4816 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 146083 8232 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_gobot-16 146100 8216 ns/op 4816 B/op 50 allocs/op BenchmarkLargeUnmarshal_gobot-16 147385 8221 ns/op 4816 B/op 50 allocs/op
BenchmarkLargeUnmarshal_telego-16 149001 7802 ns/op 6781 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 150852 8155 ns/op 6762 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 152780 7835 ns/op 6775 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 152559 8040 ns/op 6758 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 156508 7817 ns/op 6780 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 146462 7989 ns/op 6757 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 154938 7816 ns/op 6777 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 154480 7842 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 150121 7809 ns/op 6778 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 155208 7811 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 152810 7791 ns/op 6780 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 149310 7848 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 159613 7784 ns/op 6778 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 156939 7835 ns/op 6757 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 154402 7729 ns/op 6780 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 154064 7867 ns/op 6759 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 157184 7660 ns/op 6782 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 153975 7849 ns/op 6758 B/op 31 allocs/op
BenchmarkLargeUnmarshal_telego-16 155822 7768 ns/op 6780 B/op 31 allocs/op BenchmarkLargeUnmarshal_telego-16 152520 7811 ns/op 6758 B/op 31 allocs/op
BenchmarkLargeUnmarshal_echotron-16 144252 8147 ns/op 4323 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 145399 8167 ns/op 4323 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147961 8089 ns/op 4319 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 147351 8124 ns/op 4319 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149847 8049 ns/op 4320 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 148053 8094 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149128 8069 ns/op 4320 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 147170 8124 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 147277 8075 ns/op 4320 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 147792 8092 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149132 8089 ns/op 4320 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 145971 8105 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 148837 8014 ns/op 4319 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 148708 8122 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149288 8050 ns/op 4320 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 147525 8105 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 146833 8097 ns/op 4320 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 148635 8165 ns/op 4320 B/op 56 allocs/op
BenchmarkLargeUnmarshal_echotron-16 149233 8041 ns/op 4320 B/op 56 allocs/op BenchmarkLargeUnmarshal_echotron-16 150871 8124 ns/op 4320 B/op 56 allocs/op
BenchmarkWebhook_ours-16 707102 1721 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 705109 1911 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 700047 1734 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 651456 1911 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 725071 1721 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 683557 1875 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 675003 1751 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 668317 1830 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 693903 1787 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 703718 1833 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 714036 1751 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 707150 1844 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 646494 1816 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 700472 1810 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 696355 1801 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 699192 1828 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 709545 1734 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 671794 1828 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_ours-16 709700 1725 ns/op 2232 B/op 11 allocs/op BenchmarkWebhook_ours-16 687226 1826 ns/op 2232 B/op 11 allocs/op
BenchmarkWebhook_gotba-16 611132 2027 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 544628 2080 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 624046 2072 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 565494 2081 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 588478 2093 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 579736 2081 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 609744 2012 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 581365 2082 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 606912 2012 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 580501 2075 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 612211 2000 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 582160 2091 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 605488 2015 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 563130 2089 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 622398 2006 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 573872 2088 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 610478 2017 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 569616 2093 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_gotba-16 610350 2030 ns/op 1496 B/op 17 allocs/op BenchmarkWebhook_gotba-16 599923 2069 ns/op 1496 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 559515 2138 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 562394 2162 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 558510 2057 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 562137 2272 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 587257 2079 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 488323 2220 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 585321 2059 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 538561 2199 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 576236 2067 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 581037 2187 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 570172 2070 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 540055 2213 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 568270 2131 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 546921 2203 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 567567 2094 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 574988 2188 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 586588 2076 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 588076 2154 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_telebot-16 574495 2062 ns/op 1816 B/op 17 allocs/op BenchmarkWebhook_telebot-16 571617 2181 ns/op 1816 B/op 17 allocs/op
BenchmarkWebhook_gobot-16 597710 2006 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 588786 2080 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 594742 2016 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 551653 2080 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 603187 1998 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 590686 2084 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 608301 2011 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 588870 2096 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 605532 1984 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 606735 2064 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 609892 1990 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 597108 2084 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 596637 1999 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 600633 2069 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 592108 1993 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 589102 2110 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 607069 1999 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 583528 2104 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_gobot-16 594915 1997 ns/op 1832 B/op 16 allocs/op BenchmarkWebhook_gobot-16 599022 2073 ns/op 1832 B/op 16 allocs/op
BenchmarkWebhook_telego-16 603300 2021 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 587408 2193 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 602503 2016 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 601533 2152 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 618267 2016 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 584689 2127 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 601855 2027 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 576732 2153 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 612424 2035 ns/op 3132 B/op 11 allocs/op BenchmarkWebhook_telego-16 568095 2143 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 595890 2074 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 553896 2132 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 605565 2001 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 579055 2130 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 610453 2096 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 595776 2144 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 588056 2069 ns/op 3132 B/op 11 allocs/op BenchmarkWebhook_telego-16 573843 2134 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_telego-16 595764 2025 ns/op 3133 B/op 11 allocs/op BenchmarkWebhook_telego-16 513824 2267 ns/op 3131 B/op 11 allocs/op
BenchmarkWebhook_echotron-16 599088 1975 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 587734 2178 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 626964 1975 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 557188 2039 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 635306 1967 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 600524 2020 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 606621 1965 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 602959 2019 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 625998 1965 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 588694 2048 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 646352 1976 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 602366 2039 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 634186 1971 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 604621 2031 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 628503 1982 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 599146 2037 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 612586 1975 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 586056 2051 ns/op 1720 B/op 16 allocs/op
BenchmarkWebhook_echotron-16 622320 1965 ns/op 1720 B/op 16 allocs/op BenchmarkWebhook_echotron-16 598456 2049 ns/op 1720 B/op 16 allocs/op
PASS PASS
ok github.com/lukaszraczylo/go-telegram/test/benchmarks 241.963s ok github.com/lukaszraczylo/go-telegram/test/benchmarks 242.858s
? github.com/lukaszraczylo/go-telegram/test/benchmarks/shared [no test files]