В статье отражен опыт применения языков Perl и Golang в повседневной работе бородатого сисадмина в качестве скриптового языка и показаны примеры использования.

Начало времен

Когда-то давно в молодости я выбирал инструмент, который помог бы мне автоматизировать ручной труд, а именно распарсить лог или конфиг, протестировать коннект к базе данных, собрать ответы от сайтов и т.д. И я выбрал Perl. Он до сих пор является палочкой-выручалочкой. Внятное объяснение этому можно найти в данной заметке, из которой я приведу лишь главные причины актуальности Perl:

  • Он везде установлен по умолчанию, при этом удобен для быстрых сценариев и адаптируется к новым парадигмам.

  • Я могу быть уверен, что сценарий Perl, который я пишу сегодня, будет работать без изменений через 10 лет.

Любой уважающий себя сисадмин должен быть с программистским уклоном. Мне всегда было интересно писать всякого рода прокси, а также скрипты синхронизации баз данных, мониторинга, бэкапа и т.д. Для этого на CPAN можно найти кучу примеров кода с отличной документацией, которая, по моему мнению, является лучшим примером оформления и представления документации к коду.

Что-то новенькое

И вот недавно меня попросили написать скрипт (точнее я сам напросился :-)), который дергает некое api по http и результат (json) складывает в noSQL базу данных, при этом главным условием было то, что нельзя писать на Perl, т.к. в команде программистов никто его не знает. Тогда я предложил написать на Golang, ведь, по моему мнению, именно этот современный язык заслуживает внимания сисадминов. До этого момента я никогда не писал на языках со статической типизацией, да и образование у меня не программистское, но тем интересней мне показалась задача.

Так как Golang для меня новый язык, то прежде чем браться за работу (времени у меня было предостаточно) я решил напиcать скрипты на Perl и Golang для трех распространенных задач и одной не очень распространенной, тем самым сравнив некоторые моменты: скорость написания скриптов, время выполнения, потребление памяти. Вот список задач, который я собрал для примеров кода:

  1. Найти 500-е коды ответов в access.log размером ~1G

  2. Сделать выборку из sql базы данных (в таблице 12200 строк)

  3. Узнать дату выдачи и дату окончания действия ssl сертификата сайта www.example.com

  4. По особенному распарсить json

Примеры скриптов

Для замера времени выполнения все скрипты запускались через утилиту time, вывод которой будет показан после примеров кода. В момент работы скриптов в соседнем терминале была запущена команда (for i in $(seq 1 3);do ps -eo rss,command | grep script | grep -vE 'grep|vim|go run'; sleep 1 ;done), делающая замер потребления памяти (первая колонка) три раза, вывод которой так же будет показан после примеров кода.

Задача 1. Найти 500-е коды ответов в access.log размером ~1G

script.pl
#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use open qw(:std :utf8);

open FILE, "<", 'access.log' or die $!;
while (<FILE>) {
    my @a = split /\s+/;
    print if $a[8] eq '500';
}
close FILE;

script.go
package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
    "regexp"
)

func main() {
    file, err := os.Open("access.log")
    if err != nil {
        log.Fatalf("%s", err)
    }
    fileScanner := bufio.NewScanner(file)

    for fileScanner.Scan() {
        s := regexp.MustCompile(`\s+`).Split(fileScanner.Text(), 10)
        if s[8] == "500" {
            fmt.Println(s)
        }
    }
}

script.pl

script.go

Вермя выполнения

real 0m16,132s
user 0m15,758s
sys 0m0,373s

real 0m24,357s
user 0m24,375s
sys 0m0,877s

Потребление памяти (in kilobytes)

6400 perl ./script.pl
6400 perl ./script.pl
6400 perl ./script.pl

9608 ./script
9820 ./script
9540 ./script

Задача 2. Сделать выборку из sql базы данных (в таблице 12200 строк)

script.pl
#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use open qw(:std :utf8);
use DBI;

my $dbh = DBI->connect(
    "dbi:Pg:dbname='exp';host='10.10.10.1';port=5432",
    'exp', '111',
    {AutoCommit => 1, RaiseError => 1, PrintError => 0, pg_enable_utf8 => 1, ShowErrorStatement => 0}
);

my $sth = $dbh->prepare('SELECT deal_city_id, "ShortName", "FullName" FROM public.deal_city ORDER BY deal_city_id');
$sth->execute();
while (my $ref = $sth->fetchrow_hashref) {
    print $ref->{deal_city_id}.' '.$ref->{ShortName}.' '.$ref->{FullName}. "\n";
}

script.go
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

func main() {
    db, err := sql.Open("postgres", "host=10.10.10.1 port=5432 user=exp password=111 dbname=exp sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }

    rows, err := db.Query("SELECT deal_city_id, \"ShortName\", \"FullName\" FROM public.deal_city ORDER BY deal_city_id")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    var (
        deal_city_id int
        ShortName    string
        FullName     string
    )
    for rows.Next() {
        err := rows.Scan(&deal_city_id, &ShortName, &FullName)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%v %s %s\n", deal_city_id, ShortName, FullName)
    }
}

script.pl

script.go

Время выполнения

real 0m2,885s
user 0m0,071s
sys 0m0,032s

real 0m2,790s
user 0m0,030s
sys 0m0,044s

Потребление памяти (in kilobytes)

15232 perl ./script.pl
15872 perl ./script.pl
16768 perl ./script.pl

6896 ./script
9036 ./script

Задача 3. Узнать дату выдачи и дату окончания действия ssl сертификата сайта www.example.com

script.pl
#!/usr/bin/env perl

use strict;
use warnings;
use Crypt::OpenSSL::X509;
use IO::Socket::SSL;

my $client = IO::Socket::SSL->new(
    PeerHost            => "www.example.com",
    PeerPort            => 443,
    SSL_verify_callback => \&verify_cert,
) or die "error=$!, ssl_error=$SSL_ERROR";
$client->close();

sub verify_cert {
    return 1 if $_[5] != 0;
    my $cert_pem = Net::SSLeay::PEM_get_string_X509($_[4]);
    my $x509 = Crypt::OpenSSL::X509->new_from_string($cert_pem);
    print $x509->subject()   . "\n";
    print $x509->notBefore() . "\n";
    print $x509->notAfter()  . "\n";
    return 1;
}

вывод скрипта

C=US, ST=California, L=Los Angeles, O=InternetCorporationforAssignedNamesandNumbers, CN=www.example.org
Jan 13 00:00:00 2023 GMT
Feb 13 23:59:59 2024 GMT

script.go
package main

import (
    "crypto/tls"
    "fmt"
)

func main() {
    conn, err := tls.Dial("tcp", "www.example.com:443", &tls.Config{InsecureSkipVerify: true})
    if err != nil {
        panic("failed to connect: " + err.Error())
    }
    defer conn.Close()

    cs := conn.ConnectionState()
    for _, cert := range cs.PeerCertificates {
        fmt.Printf("%v\n", cert.Subject)
        fmt.Printf("%v\n", cert.NotBefore)
        fmt.Printf("%v\n", cert.NotAfter)
        break
    }
}

вывод скрипта

CN=www.example.org,O=Internet Corporation for Assigned Names and Numbers,L=Los Angeles,ST=California,C=US
2023-01-13 00:00:00 +0000 UTC
2024-02-13 23:59:59 +0000 UTC

script.pl

script.go

Время выполнения

real 0m0,531s
user 0m0,051s
sys 0m0,012s

real 0m0,441s
user 0m0,002s
sys 0m0,004s

Потребление памяти (in kilobytes)

22732 perl ./script.pl

9900 ./script

Задача 4. По особенному распарсить json

Эта задача родилась уже после того, как я написал тот самый скрипт, который меня попросили. Дело тут в том, что Perl просто берет json строку и парсит ее целиком в свои структуры (массивы, хеши) и дальше ты работаешь уже с ними. В Golang все немного сложнее, прежде чем парсить нужно самому описать весь json (все объекты!) в типах, с которыми дальше удобно работать. Вот тут есть статья, описывающая нюансы парсинга json в Golang, прочитав которую можно понять весь масштаб трагедии для человека писавшего всю жизнь на языке с динамическими типами. Хорошо, что в моем скрипте мне не надо было глубоко парсить json, а хватило лишь разбить верхний json массив на строки, которые представляют json объект. В приведенных ниже скриптах показан пример такого подхода, сначала на Golang, а уже потом, ради интереса, повторенный на Perl, поэтому сначала представлен скрипт на Golang, а уже потом на Perl, в отличие от других задач, в которых последовательность написания скриптов была другой.

script.go
package main

import (
    "encoding/json"
    "fmt"
)

type Developer struct {
    RawValue string
}

func (d *Developer) UnmarshalJSON(data []byte) error {
    d.RawValue = string(data)
    return nil
}

func main() {
    jsonStr := `[
        {"id":1,"name":"Larry"},
        {"id":2,"name":"Robert"},
        {"id":3,"name":"Rob"},
        {"id":4,"name":"Ken"}
    ]`

    developers := []Developer{}
    if err := json.Unmarshal([]byte(jsonStr), &developers); err != nil {
        panic(err)
    }
    for _, d := range developers {
        //fmt.Printf("%s --- is %T\n", d.RawValue, d.RawValue)
        fmt.Printf("%s\n", d.RawValue)
    }
}

вывод скрипта

{"id":1,"name":"Larry"}
{"id":2,"name":"Robert"}
{"id":3,"name":"Rob"}
{"id":4,"name":"Ken"}

script.pl
#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use open qw(:std :utf8);
use JSON ();

my $jsonStr = '[
    {"id":1,"name":"Larry"},
    {"id":2,"name":"Robert"},
    {"id":3,"name":"Rob"},
    {"id":4,"name":"Ken"}
]';

my $developers = eval {
    JSON
    ->new
    ->filter_json_object(sub{JSON::encode_json(shift)})
    ->decode($jsonStr)
};
die $@ if $@;
print $_ . "\n" for @$developers;

вывод скрипта

{"id":1,"name":"Larry"}
{"id":2,"name":"Robert"}
{"name":"Rob","id":3}
{"name":"Ken","id":4}

В этой задаче не будет таблицы с замерами, т.к. это бессмысленно. Взглянув на скрипты, можно увидеть, что подходы совершенно разные. Golang не пытается распарсить весь json, а разбивает лишь верхний массив на строки. Perl сначала парсит массив, затем парсит объекты внутри него в свои хеши и уже затем просто заменяет их на то, что ему подсунули, а подсунули ему обратно сериализованный хеш, и поэтому в выводе скрипта ключи внутри некоторых json объектов перемешаны.

Выводы

Начнем с самого простого - скорость написания скриптов. У обоих языков хорошая документация и куча примеров кода на все случаи, поэтому этот критерий зависит только от практики, и зависимость тут линейная - чем больше практики, тем быстрее скорость написания скриптов.

При работе с текстом (задача 1) Perl оказался на высоте: он и памяти меньше потребляет, и быстрее работает. Вот тут есть классные тесты по потреблению памяти, в которых подтверждается превосходство Perl при работе с текстом. Возможно, приведенный мною код на Golang не сильно оптимизированный и можно его улучшить, чтобы сократить отставание. Я буду только рад, если в комментариях предложат вариант пооптимальней. Нужно сделать еще одну оговорку: в данной задаче в варианте на Perl я не использовал ни одного модуля, и если вдруг окажется, что для распарсивания лога будет нужен модуль (например DateTime), то считаю, что Perl сравняется по скорости с Golang и проиграет по памяти, как это происходит в других задачах.

Во всех других задачах Perl чуток уступает по скорости выполнения и сильно проигрывает по памяти, что вполне объяснимо - Golang язык со строгой статической типизацией, а следовательно, должен потреблять меньше памяти.

Главные выводы из всего этого можно сделать такие:

  • изучать Golang после многолетнего использования Perl сложновато, но вполне по силам сисадминам;

  • Golang вполне способен заменить Perl как скриптовый язык.

Комментарии (30)


  1. fireSparrow
    29.06.2023 11:47
    +15

    Можете считать это глубоким имхо, но я должен это написать:


    • сейчас рекомендовать Perl в качестве скриптового языка в сфере системного администрирования стоит только тем, кто уже знает Perl, и почему-то принципиально против изучения питона
    • рекомендовать Go в качестве скриптового языка в сфере системного администрирования стоит только тем, кто в своих специфических задачах упирается в производительность, и не может найти подходящего модуля для питона
    • всем остальным 99,9% системных администраторов всё-таки стоит взять питон


    1. krpsh Автор
      29.06.2023 11:47
      +2

      • А я в статье и не призываю никого учить Perl и ничего против питона не имею.

      • В точку про производительность!

      • Скорее всего соглашусь с вами


      1. fireSparrow
        29.06.2023 11:47
        +3

        А я в статье и не призываю никого учить Perl и ничего против питона не имею

        Я просто счёл нужным оставить этот коммент на тот случай, если статью будет читать кто-то, кто пока не ориентируется в языках, но присматривает подходящий инструмент автоматизации )


        1. krpsh Автор
          29.06.2023 11:47

          Кстати, а нет ли у вас желания воспроизвести такие же скрипты на питоне (можно без замеров), чтобы можно было наглядно сравнить?


          1. fireSparrow
            29.06.2023 11:47
            +2

            Хорошая мысль, я займусь этим в выходные.


          1. fireSparrow
            29.06.2023 11:47

            Вот так получается на питоне:


            Разбор логов
            import re
            
            separator = re.compile('\s+')
            
            with open("access.log") as log_file:
                for line in log_file:
                    parts = re.split(separator, line)
                    if parts[8] == '500':
                        print(line)

            Работа с базой
            import psycopg2
            
            conn = psycopg2.connect(dbname='database', user='db_user', password='mypassword', host='localhost')
            cursor = conn.cursor()
            
            cursor.execute('SELECT deal_city_id, "ShortName", "FullName" FROM public.deal_city ORDER BY deal_city_id')
            
            for row in cursor:
                print(f"{row['deal_city_id']} {row['ShortName']} {row['FullName']}")
            
            cursor.close()
            conn.close()

            Получение информации о сертефикате
            import OpenSSL
            import ssl
            
            cert = ssl.get_server_certificate(('www.example.com', 443))
            x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
            
            print(x509.get_subject())
            print(x509.get_notBefore())
            print(x509.get_notAfter())

            Работа с джсоном
            import json
            
            json_str = '''[
                {"id":1,"name":"Larry"},
                {"id":2,"name":"Robert"},
                {"id":3,"name":"Rob"},
                {"id":4,"name":"Ken"}
            ]'''
            
            developers = json.loads(json_str)
            
            for d in developers:
                print(d)


            1. krpsh Автор
              29.06.2023 11:47

              Разбор логов

              По умолчанию print ставит перевод строки, поэтому я чуток поправил вот так print(line , end='')

              real    0m19,840s
              user    0m18,834s
              sys     0m1,006s
              
              9728 python ./script.py
              9728 python ./script.py
              9728 python ./script.py

              Работа с базой

              У меня ошибка (

              Traceback (most recent call last):
                File "/home/pasha/src_my/habr_perl_golang/./task2/script.py", line 11, in <module>
                  print(f"{row['deal_city_id']} {row['ShortName']} {row['FullName']}")
                           ~~~^^^^^^^^^^^^^^^^
              TypeError: tuple indices must be integers or slices, not str

              Пришлось поправить вывод результата вот так print(row)

              real    0m2,338s
              user    0m0,061s
              sys     0m0,053s
              
              19284 python ./script.py
              20308 python ./script.py

              Получение информации о сертефикате

              real    0m0,475s
              user    0m0,054s
              sys     0m0,011s
              
              25668 python ./script.py

              вывод скрипта

              <X509Name object '/C=US/ST=California/L=Los Angeles/O=Internet\xC2\xA0Corporation\xC2\xA0for\xC2\xA0Assigned\xC2\xA0Names\xC2\xA0and\xC2\xA0Numbers/CN=www.example.org'>
              b'20230113000000Z'
              b'20240213235959Z'

              Работа с джсоном

              Тут я прям сильно удивился простоте скрипта. Вы, наверное, не поняли сути задачи, ибо в массиве developers явно находятся не строки, а какие-то структуры, т.к. каждая строка вывода скрипта не является валидным json объектом.

              вывод скрипта

              {'id': 1, 'name': 'Larry'}
              {'id': 2, 'name': 'Robert'}
              {'id': 3, 'name': 'Rob'}
              {'id': 4, 'name': 'Ken'}


  1. borisovEvg
    29.06.2023 11:47
    +9

    perl навсегда в моем сердечке????


  1. kale
    29.06.2023 11:47
    +2

    Компилировать regex конечно надо вне цикла, да и в целом тесты производительности с выводом на внешнее устройство такое себе.. Но perl удивил, да.


    1. krpsh Автор
      29.06.2023 11:47

      Компилировать regex конечно надо вне цикла

      точно, а вы ведь правы

      я проверил и Golang отработал быстрее (везде он чуток, но быстрее, елки зеленые :-)), но по памяти проиграл

      real    0m15,796s
      user    0m15,603s
      sys     0m0,691s
       8232 /tmp/go-build260055243/b001/exe/script
       8300 /tmp/go-build260055243/b001/exe/script
       8120 /tmp/go-build260055243/b001/exe/script
      


      1. noRoman
        29.06.2023 11:47
        +1

        /занудаon/ Решение немножко в лоб в коде. Так будет шустрее. /занудаoff/

        package main
        
        import (
        	"bufio"
        	"fmt"
        	"log"
        	"os"
        	"regexp"
        )
        
        func main() {
        	file, err := os.Open("access.log")
        	if err != nil {
        		log.Fatalf("%s", err)
        	}
        	fileScanner := bufio.NewScanner(file)
        
        	re := regexp.MustCompile(`(?s)(?:.+?\s){8}(.+?)\s`)
        	for fileScanner.Scan() {
        		s := re.FindStringSubmatch(fileScanner.Text())
        		if s[1] == "500" {
        			fmt.Println(s)
        		}
        	}
        }
        


        1. krpsh Автор
          29.06.2023 11:47

          Да, так работает быстрее

          real    0m8,611s
          user    0m8,331s
          sys     0m0,539s

          Но надо сделать оговорку, что в данной регулярке вы отбрасываете все, что идет после кода ответа сервера.

          И если захватывать остаток строки вот так

          re := regexp.MustCompile(`(?s)(?:.+?\s){8}(.+?)\s(.+)`)

          то результат будет другой

          real    0m14,968s
          user    0m14,719s
          sys     0m0,560s


          1. noRoman
            29.06.2023 11:47

            Я по "ТЗ" ориентировался. Зачем собирать лишнее, если оно не требуется)


  1. PaperBread
    29.06.2023 11:47

    В golang необязательно описывать весь json, можно только те поля что вас интересуют.

    https://go.dev/play/p/7A3nV5CayAm


    1. krpsh Автор
      29.06.2023 11:47

      Я знаю, что так можно, но как раз в этом-то и заключается ключевое отличие. В Perl ты получаешь сразу весь десериализованный json, а в Golang ты должен его описать настолько подробно, насколько тебе надо прежде чем десериализовать. Этот момент понять было сложнее всего.

      И да, у Perl то же есть песочница )


      1. RumataEstora
        29.06.2023 11:47

        Вот еще... Целая игровая площадка с разными песочницами

        https://onecompiler.com


  1. kolezz
    29.06.2023 11:47

    смысл сравнения изначально неправилен.

    perl силён в операциях со строками, go - в многопоточности.
    если в go работа исключительно со строками (или внешними сервисами) и без горутин - это признак неправильного выбора инструмента.

    это не прямые конкуренты, а инструменты, заточенные каждый на свою область.


    1. krpsh Автор
      29.06.2023 11:47

      Соглашусь с вами - это не конкуренты, я этого и не утверждал. Просто мне было интересно, можно ли использовать Golang (не слишком ли он сложен) как сриптовый язык для простых нужд сисадмина. Да, горутины классные, я пробовал, но скажу вам, что для рядового сисадмина горутины почти не нужны, да и на Perl есть куча модулей позволяющих так же элегантно породить (fork) кучу воркеров. Конечно же fork не сравнится с горутинами, но например для опроса кучи сайтов можно использовать не воркеры, а асинхронно это сделать.

      И да, для всего нужен свой инструмент и правильный сисадмин умеет его выбирать под задачу )


      1. domix32
        29.06.2023 11:47

        можно ли использовать Golang как сриптовый язык

        Так использовали б его для большего юзабилити вроде вот такого.

        Мне кстати интересно сколько времени займёт grep/ripgrep/awk для подобной задачи.

        rg "\s500\s" ./access.log


        1. krpsh Автор
          29.06.2023 11:47

          time grep -E "\s500\s" ./access.log

          real    0m1,165s
          user    0m1,051s
          sys     0m0,114s

          time awk '$9~500' ./access.log

          real    0m1,258s
          user    0m1,077s
          sys     0m0,182s

          если что, то в логе вот сколько строк

          wc -l ./access.log

          3984116 ./access.log

          ripgrep - не встречал такого (


          1. domix32
            29.06.2023 11:47
            +1

            ripgrep отсутвует в дефолтных дистрибутивах обычно, если это не какой-ниубдь gentoo.

            `apt install ripgrep` должен сработать или через что вы там ставите. Если пользоваться также как и обычным грепом, то в среднем скорость х5 получается, иногда выше.


            1. krpsh Автор
              29.06.2023 11:47

              уговорили )))

              time rg "\s500\s" ./access.log

              real	0m0,208s
              user	0m0,135s
              sys	    0m0,073s

              да, действительно, реактивный какой )

              спасибо, за полезный инструмент!


          1. domix32
            29.06.2023 11:47

            Если вывод никуда не перенаправляется - вывод ripgrep примерно вдвое медленее. Если отправлять его в /dev/null grep кажется вообще ничего не делает и выводит примерно нисколько времени. Если делать вывод в файл, то grep выдаёт

            real    0m2.240s
            user    0m2.032s
            sys     0m0.204s

            а rg

            real    0m0.430s
            user    0m0.312s
            sys     0m0.112s

            что в примерно равно среднему времени.

            awk выдал что-то близкое к вашим цифрам

            Данных генерил как-то так
            import random
            
            request_methods = ["GET", "POST", "PUT", "DELETE"]
            urls = ["/page1", "/page2", "/page3", "/page4"]
            status_codes = [200, 200, 200, 200, 200, 404, 500, 500]
            
            with open("access_log.txt", "w") as file:
                for _ in range(3984116):
                    ip_address = "127.0.0.1"  # Example IP address
            
                    # Randomly select request method, URL, and status code
                    request_method = random.choice(request_methods)
                    url = random.choice(urls)
                    status_code = random.choice(status_codes)
            
                    # Determine the response size based on the status code
                    if status_code == 200:
                        response_size = random.randint(1024, 4096)
                    elif status_code == 500:
                        response_size = random.randint(512, 2048)
                    else:
                        response_size = 0
            
                    # Generate the log entry and write it to the file
                    log_entry = f"{ip_address} - - [30/Jun/2023:12:00:00 +0000] \"{request_method} {url} HTTP/1.1\" {status_code} {response_size}\n"
                    file.write(log_entry)

            правда размер лога примерно 300 Мб получился.


            1. krpsh Автор
              29.06.2023 11:47

              • Я access.log не генерил, а взял с рабочего сервера

              • У меня 500-х кодов 226 строк

              • Во всех тестах вывод был на терминал, никуда ничего не перенаправлял


              1. domix32
                29.06.2023 11:47

                Ну, у меня прост не было логов, потому сгенерировал столько же строк, чтобы было чутка чеснее. Правда 500 там было 995570 штук и выводило их 20+/-5 секунд. C 250 штук и выводом в терминал grep стал выводить как rg выше - в районе 0.3 сек, а rg выдал

                real    0m0.084s
                user    0m0.064s
                sys     0m0.020s


        1. krpsh Автор
          29.06.2023 11:47
          +1

          Так использовали б его для большего юзабилити вроде вот такого.

          поржал )))))


    1. lrrr11
      29.06.2023 11:47
      +1

      если в go работа исключительно со строками (или внешними сервисами) и без горутин - это признак неправильного выбора инструмента

      сами разработчики go из Google используют этот язык в том числе и именно так, в boringssl например: https://github.com/google/boringssl/tree/master/crypto/obj

      причем на go тут были переписаны как раз перловые скрипты из openssl


    1. domix32
      29.06.2023 11:47

      А уж бенчить коннекты к sql базе и подавно бред.


      1. krpsh Автор
        29.06.2023 11:47

        Ну почему же. В данном случае это оправдано, т.к. все развернуто локально и база в том числе, а это значит, что она в момент теста не была нагуржена от слова совсем и замеры проводились несколько раз чтобы прогреть всякие буферы, кеши и прочее.


        1. domix32
          29.06.2023 11:47

          Движок SQL вроде в итоге получается ровно тот же самый и фактическая разница это собственно копирование данных из базы в контекст программы, что может иметь некоторые накладные расходы от выбранного языка, и собственно IO, производительность которого в принципе непредсказуема. Разница скорость исполнения запроса в таком случае должна иметь чисто статистическую погрешность. Вот если бы это всё поверх ORM как-то происходило, вот тогда б можно было пободаться у кого он быстрее.