diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 9cb0c86..06750fe 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -42,6 +42,7 @@ jobs: - run: mix deps.get --only $MIX_ENV - run: mix compile --warnings-as-errors - run: mkdir results + - run: mix run bench/encode.exs | tee results/encode.txt - run: mix run bench/insert.exs | tee results/insert.txt - run: mix run bench/stream.exs | tee results/stream.txt - uses: actions/upload-artifact@v4 diff --git a/bench/encode.exs b/bench/encode.exs new file mode 100644 index 0000000..4c8f160 --- /dev/null +++ b/bench/encode.exs @@ -0,0 +1,35 @@ +types = [Ch.Types.u64(), Ch.Types.string(), Ch.Types.array(Ch.Types.u8()), Ch.Types.datetime()] +encoding_types = Ch.RowBinary.encoding_types(types) + +rows = fn count -> + Enum.map(1..count, fn i -> + [i, "Golang SQL database driver", [1, 2, 3, 4, 5, 6, 7, 8, 9], NaiveDateTime.utc_now()] + end) +end + +alias Ch.{RowBinary, Native} + +defmodule NativeBuffer do + def encode_row(row, buffer) do + Native.add_row(buffer, row) + end + + def encode_rows(rows, types) do + rows + |> Enum.reduce(Native.new_buffer(types), &__MODULE__.encode_row/2) + |> Native.to_iodata() + end +end + +Benchee.run( + %{ + "control" => fn rows -> Enum.each(rows, fn _row -> [] end) end, + "RowBinary.encode_rows/2" => fn rows -> RowBinary.encode_rows(rows, types) end, + "RowBinary._encode_rows/2" => fn rows -> RowBinary._encode_rows(rows, encoding_types) end, + "Native.add_row/2" => fn rows -> NativeBuffer.encode_rows(rows, types) end + }, + inputs: %{ + # TODO more inputs (take some from Plausible write buffer) + "1_000_000 rows" => rows.(1_000_000) + } +) diff --git a/lib/ch/native.ex b/lib/ch/native.ex new file mode 100644 index 0000000..e2b956b --- /dev/null +++ b/lib/ch/native.ex @@ -0,0 +1,27 @@ +defmodule Ch.Native do + @moduledoc """ + Helpers for working with ClickHouse [`Native`](https://clickhouse.com/docs/en/sql-reference/formats#native) format. + """ + + @opaque buffer :: list() + + @spec new_buffer([String.t()]) :: buffer + def new_buffer(types) do + encoding_types = Ch.RowBinary.encoding_types(types) + Enum.map(encoding_types, fn t -> [t | _column = []] end) + end + + @spec add_row(buffer, [term]) :: buffer + def add_row(buffer, row) + + def add_row([[type | column] | buffer_rest], [value | row_rest]) do + [[type | [column | Ch.RowBinary.encode(type, value)]] | add_row(buffer_rest, row_rest)] + end + + def add_row([] = done, []), do: done + + @spec to_iodata(buffer) :: iodata + def to_iodata(buffer) do + Enum.map(buffer, fn [_type | column] -> column end) + end +end