惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
Recorded Future
Recorded Future
T
Tenable Blog
S
Securelist
C
CERT Recently Published Vulnerability Notes
T
Threatpost
S
Schneier on Security
A
Arctic Wolf
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Register - Security
The Register - Security
Cisco Talos Blog
Cisco Talos Blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
T
True Tiger Recordings
T
Threat Research - Cisco Blogs
V
Vulnerabilities – Threatpost
P
Palo Alto Networks Blog
T
The Exploit Database - CXSecurity.com
小众软件
小众软件
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Azure Blog
Microsoft Azure Blog
Cyberwarzone
Cyberwarzone
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tor Project blog
Spread Privacy
Spread Privacy
Malwarebytes
Malwarebytes
P
Proofpoint News Feed
F
Fox-IT International blog
F
Fortinet All Blogs
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
量子位
Latest news
Latest news
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
Project Zero
Project Zero
T
Tailwind CSS Blog
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
I
Intezer
博客园_首页
腾讯CDC
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Darknet – Hacking Tools, Hacker News & Cyber Security

Все публикации подряд на Хабре

Idempotency keys: 5 граблей, которые мы поймали на проде Gamedev. Парсинг данных из Google Sheets и Excel в json без привлечения программистов Nano Banana Google AI: как использовать Нано Банана для генерации и редактирования изображений Два игрока на весь российский рынок ИИ: что показал ЦИПР-2026 Менеджер ресурсов ЯНДЕКС 360 (YANDEX 360) промокоды июнь 2026: промокод Yandex 360 скидка 40% на годовые тарифы Open-Source инструмент для автоматического перевода книг Ищу ранних тестировщиков для Android-версии agent harnesses Не используйте LLM для текста Увеличиваем продажи без слез аналитика Оптимизация запросов к PostgreSQL: 5 неочевидных настроек для продакшена 45 лет тюрьмы за DROP TABLE и переход Карпатого в Anthropic Планирование движения для ровера на ходовой Ackerman'а Революция в изучении языков Java — быстрая. Ваш код может таким не быть Как я опоздал на конкурс OpenAi с новой архитектурой нейросети Быстрые интеграции в 1С: прощайте, бесконечные переделки Как получить субсидию 300 миллионов от Минпромторга? preIPO Anthropic, OpenAI, SpaceX. Разбираемся — стоит ли участвовать? Entaxy ION + OPC UA: два способа получить данные с промышленного оборудования Память на миллион, а толку ноль: как мы спасали ИИ-агента от «тупости» РСЯ, AdSense или myTarget: что на самом деле в 2026 приносит больше денег сайту и причем тут монетизаторы Практическое построение сервисов на Go под реальный трафик PostgreSQL и аналитика: что меняется, когда хранилище становится общим Codex за 5 месяцев 2026: мой топ-5 релизов, что не зашло и где OpenAI обогнал Anthropic Как создать короткое видео с помощью нейросетей: Полный гайд по Veo 3.1, Kling 3.0 и Happy Horse 1.0 Алгоритм проверок физлиц от экс сотрудника ФНС Как ИИ портит резюме студентам Системные вызовы в сфере ИТ в 2026: стратегический взгляд для ИТ-руководителей Вайбкодинг заканчивается на localhost: как я строю SaaS для цифровизации коттеджных поселков с Codex Производственные риски в небольшом кастомном производстве. С чем я сталкивалась и как научилась это учитывать Подключаем ИИ органы чувств: bash-демон, пайка и самосознание на Raspberry Pi Я хотел повторить Growing Neural CA за вечер. Ушёл месяц Промт для генерации текста без ИИ следа — как писать уникальные тексты через нейросеть От capabilities к AppArmor: что реально остановит атакующего в контейнере CactOS Вектора интересов: как находить настоящую мотивацию и усиливать команды Цена безопасности [Перевод] Цена безопасности “Рубик” от пет-проекта до прода или ITIL 4 для строительно-торговых центров Чего ждать (и не ждать) от ремейка AC4 Black Flag Архитектурный тупик корпоративного хранения: почему смена модели не снимает ограничений и что с этим делать Атаки через подрядчиков, дефицит кадров и квест с импортозамещением: главные вызовы ИБ в 2026 году Я не оставлю детям наследства Почему порты стали «дверями» в сервер, и кто решил, что SSH будет 22 Почему зарубежные разработчики чипов возвращаются на китайские фабрики Как у меня НЕ получился торговый бот на Polymarket Проектирование архитектуры в нотации ArchiMate с использованием ИИ. Часть 2 Как превратить домашнюю файлопомойку в умную AI-галерею на основе сборки из x99+Xeon и видеокарты за 2 тыс рублей Перспективы заселения нашей галактики Кризис менеджмент в ИТ Reactive Programming не спасёт вас. Если вы не решили эти 5 проблем — у вас просто медленный монолит с Flux Как я делаю DIY-контроллер для ПК: громкость, приложения, MIDI, OBS Миграция микросервисов на Python с помощью LLM: экономим месяцы для разработчиков Программирование микросхем GAL и им подобных Почему таск-трекер не заменяет ИСУП: из чего состоит полноценный контур управления проектами Всё об информационной безопасности. Кибербезопасность. DevOps, CI/CD. Хакеры. Алексей Федулаев Как импортировать базу клиентов в amoCRM и навести порядок в контактах Как мы четыре раза переписали Outbox Google предлагает единый «водяной знак» для изображений, видео и текста, созданных ИИ Сексизм в IT: данные вместо домыслов Один фронтенд, чтоб править всеми, один фронтенд, чтоб всех найти: 1 точка входа, разные BI ИИ в тестировании: зачем мы пошли в пилот и почему начали с чата, а не с агентов Как я научила Telegram-бота наводить порядок в чате с мемами: пересылка по хештегам в соответствующую тему Как мы сделали внутреннюю CRM для управления студией – опыт Doubletapp Десятипальцевый метод — как печатать цифру " Шесть "? Партнерская программа по нейросетям: зарабатывай на ИИ, приводя клиентов в AI-сервис Как я сделал «клик по элементу → открыть в VS Code» за один вечер Эволюция Telegram‑бота на C++: от «лапши» в main() до ООП, in‑memory кэша и мутов по Фибоначчи Как я (внезапно) стал адвокатом вайб‑кодинга в корпорации Дизайн за 5 минут. Дайджест мая 2026 Только 17% всех 64-битных целых чисел можно разложить на два 32-битных 0,000000001% × ∞ = 100%. Вы осознаёте что любое событие неизбежно? «Вы либо трусы наденьте, либо крестик снимите». Как мы выиграли еще один суд против PR-агентства PRslon Почему вы тратите время не на переговоры, а на чужую внутреннюю драму. Как проходят переговоры с крупными компаниями Как приоритизировать регрессионные проверки, когда сжаты сроки релиза Электронные транспортные накладные: технический разбор нововведений 2026 года для логистов, разработчиков и бизнеса Как определить LLM под капотом чат-бота: учебный эксперимент по black-box fingerprinting Хабру 20 лет — зовём вас отметить это к нам Домой iPad как инструмент разработчика в эпоху агентного программирования Inspector v3: как я сделал свой центр управления Kubernetes на старом ноутбуке Как мы осваивали производство гибко-жёстких печатных плат: от проб и ошибок к рабочей технологии 30 лет мы внедряли в России Ansys. А потом он ушёл — и пришлось садиться писать собственный CAE для аддитивной печати Цифровой рубль и цифровой чек Облако под защитой от DDoS: чем On-Demand отличается от Always-On Распродажа в издательстве «Питер» Почему современный стадион больше похож на ЦОД, чем на арену Машина, которая учится думать Запихнули игровую приставку в короб и в первый же месяц продали на 3 млн Игровой ноутбук vs игровой ПК за те же деньги: что изменилось в 2026 году ГИС для Minecraft. Часть 1 Смена старого оборудования на новое убирает огромные затраты на его эксплуатацию — но куда девать всё это старое? Project Manager 2026: как AI-инструменты меняют профессию SLA как инструмент, а не отчёт. Часть 1. Как подружить бизнес и инженеров через общие цифры Послания от ангелов и первый шаг к компьютерам: стеганография Средневековья и Ренессанса Что новенького есть в CSS в 2026 году? Хватит мучить ChatGPT. Почему ваш промпт не сработает Как и зачем мы писали семантический слой для ИИ аналитики – SLayer Маленькая EVPN/VXLAN-фабрика без тупика: как мы запускали площадку в Амстердаме Репликация по DDIA: что я понял, только когда сам сломал прод
Ускоряем и оптимизируем numpy, pandas, scipy и sklearn
mike1 · 2026-05-27 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение10 мин

Охват и читатели837

Обзор

С момента публикации статьи на Хабре «Импортозамещаем numpy, pandas, scipy и sklearn» прошло почти три года. В течение этого времени я приостановил работу над проектом из-за нехватки времени, ресурсов и сил. К тому же, меня расстроило, что не смог выполнить просьбу пользователя @N-Cube, который активно интересовался моей библиотекой и хотел ускорить работу своего Jupyter Notebook.

В самый критический момент на помощь пришел волшебный AI, который, хоть и иногда проявлял недостаток гибкости, с готовностью исполнял все пожелания своего хозяина. Благодаря этому проект начал продвигаться вперед.

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

Давайте рассмотрим, что на сегодняшний день позволяет сделать моя библиотека.

Я представлю несколько тестовых примеров в двух вариантах: с использованием AVX-2 на процессоре Intel® Core™ i7-4790K и AVX-512 на Intel® Xeon. Также покажу результаты замеров для каждого из них. Все тесты проводились без использования GPU, исключительно на процессоре. Это позволяет сравнивать производительность Python и моей библиотеки на равных условиях. Операционная система – Ubuntu 24.04, компилятор – GNU 13.3.0.

Метод Монте-Карло для вычисления числа π

Скрытый текст
  • Генерация координат: Программа создает два вектора случайных координат: rx и ry, которые находятся в диапазоне от 0 до 1. Эти координаты представляют собой точки на плоскости.

  • Проверка попадания: Точка считается находящейся внутри круга, если расстояние dist от начала координат (0, 0) меньше радиуса, равного 1. Это условие можно проверить с помощью следующей формулы: rx² + ry² < 1.

  • Вычисление: Итоговая оценка числа π (pi_est) вычисляется как отношение количества точек, попавших внутрь круга, к общему количеству сгенерированных точек.

Бенчмарк: https://github.com/mgorshkov/np/blob/main/samples/monte-carlo/compare_python_monte_carlo.py

Оригинальный питоновский код

rx = np.random.rand(size)
ry = np.random.rand(size)
dist = rx * rx + ry * ry
inside = np.sum(dist < 1.0)
pi_est = 4.0 * inside / size

Код из библиотеки

auto rx = random::rand(size);
auto ry = random::rand(size);
auto dist = rx * rx + ry * ry;
auto inside = sum("dist<1", dist);
double pi_est = 4 * static_cast<double>(inside) / size;

Результаты бенчмарка на AVX-2

Size

Py time (us)

Py mem (MiB)

C++ time (us)

C++ mem (MiB)

Speedup

Mem ratio

100000

4222

2.3

638

1.5

6.62x

1.5x

1000000

19760

22.9

3386

15.3

5.84x

1.5x

10000000

181804

228.9

29889

152.6

6.08x

1.5x

100000000

1770601

2288.8

313803

1525.9

5.64x

1.5x

Результаты бенчмарка на AVX-512

Size

Py time (us)

Py mem (MiB)

C++ time (us)

C++ mem (MiB)

Speedup

Mem ratio

100000

7538

2.3

2371

1.5

3.18x

1.5x

1000000

30011

22.9

3782

15.3

7.94x

1.5x

10000000

235035

228.9

23761

152.6

9.89x

1.5x

100000000

6192049

2288.8

285586

1525.9

21.68x

1.5x

Неполная бета-функция

Скрытый текст

Что такое полная бета-функция?

Чтобы понять неполную версию, нужно вспомнить полную. Полная бета-функция $\(B(a, b)\)$ — это определенный интеграл от нуля до единицы, который зависит от двух параметров $\(a\)$ и $\(b\)$:

$$\(B(a, b) = \int_{0}^{1} t^{a-1} (1-t)^{b-1} \, dt\)$$

Что такое неполная бета-функция?

В неполной бета-функции верхний предел интеграла заменяется на переменную $\(x\)$ (где $\(0 \le x \le 1\))$. Это значит, что мы интегрируем функцию не до конца, а только на отрезке от $\(0\)$ до $\(x\)$.

Обозначается она как $\(B_x(a, b)\)$ и определяется следующим образом:

$$\(B_x(a, b) = \int_{0}^{x} t^{a-1} (1-t)^{b-1} \, dt\)$$

Бенчмарк: https://github.com/mgorshkov/scipy/tree/main/benchmarks/betainc

Оригинальный код на Python

#!/usr/bin/env python3
"""
Python scipy betainc benchmark - called by the C++ comparison benchmark.
Uses the same test parameters as the C++ benchmark for fair comparison.
"""
import time
import sys
import scipy.special


def benchmark_python_scipy():
    a = 0.5 * 99997
    b = 0.5 * 99997
    x = 0.4
    count = 0
    res = 0.0

    start = time.perf_counter_ns()

    while x < 0.6:
        count += 1
        res += scipy.special.betainc(a, b, x)
        x += 0.000001

    stop = time.perf_counter_ns()

    diff = stop - start
    print(f"Result = {res}")
    print(f"Time = {diff} ns")
    print(f"Loops = {count}")


if __name__ == "__main__":
    benchmark_python_scipy()

Код из библиотеки

timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);

np::float_ a = 0.5 * 99997;
np::float_ b = 0.5 * 99997;

np::float_ x = 0.4;
int count = 0;
np::float_ res = 0;

while (x < 0.6) {
    ++count;
    res += scipy::special::betainc(a, b, x);
    x += 0.000001;
}

timespec stop;
clock_gettime(CLOCK_MONOTONIC, &stop);

std::uint64_t diff = 1000000000L * (stop.tv_sec - start.tv_sec) + stop.tv_nsec - start.tv_nsec;

std::cout << "Result = " << res << std::endl;
std::cout << "Time = " << diff << " ns" << std::endl;
std::cout << "Loops = " << count << std::endl;

Результаты бенчмарка на AVX-2

Implementation

Time (ns)

Loops

Speedup vs Python

C++ scipy (AVX2)

115882110

200000

2.26x

Python scipy

262307821

200000

1.00x

Результаты бенчмарка на AVX-512

Implementation

Time (ns)

Loops

Speedup vs Python

C++ scipy (AVX512)

113440191

200000

2.75x

Python scipy

311787699

200000

1.00x

Большой фрагмент оптимизированного Jupyter Notebook (основные компоненты - неполная бета-функция и линейная регрессия)

Оригинальный код на Python из комментария к предыдущей статье: https://habr.com/ru/articles/752762/#comment_25829022

Бенчмарк: https://github.com/mgorshkov/sklearn/blob/main/samples/gmt_trend_2d/benchmark.cpp

Код на Python

Скрытый текст
from tabulate import tabulate
import numpy as np

def generate_data(rank, num_points, noise_level):
    np.random.seed(42)
    x = np.linspace(-10, 10, num_points)
    y = np.linspace(-10, 10, num_points)
    if rank == 1:
        z = 3 * x + 5 + noise_level * np.random.randn(num_points)
        data = np.column_stack((x, y, z))
    elif rank == 2:
        z = 2 * x + 3 * y + 5 + noise_level * np.random.randn(num_points)
        data = np.column_stack((x, y, z))
    elif rank == 3:
        z = 2 * x**2 + 3 * y**2 + 5 + noise_level * np.random.randn(num_points)
        data = np.column_stack((x, y, z))
    return data

def GMT_trend2d(data, rank):
    import numpy as np
    from sklearn.linear_model import LinearRegression
    # scale factor for normally distributed data is 1.4826
    # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.median_abs_deviation.html
    MAD_NORMALIZE = 1.4826
    # significance value
    sig_threshold = 0.51

    if rank not in [1,2,3]:
        raise Exception('Number of model parameters "rank" should be 1, 2, or 3')

    #see gmt_stat.c
    def gmtstat_f_q (chisq1, nu1, chisq2, nu2):
        import scipy.special as sc

        if chisq1 == 0.0:
            return 1
        if chisq2 == 0.0:
            return 0
        return sc.betainc(0.5*nu2, 0.5*nu1, chisq2/(chisq2+chisq1))

    if rank in [2,3]:
        x = data[:,0]
        x = np.interp(x, (x.min(), x.max()), (-1, +1))
    if rank == 3:
        y = data[:,1]
        y = np.interp(y, (y.min(), y.max()), (-1, +1))
    z = data[:,2]
    w = np.ones(z.shape)

    if rank == 1:
        xy = np.expand_dims(np.zeros(z.shape),1)
    elif rank == 2:
        xy = np.expand_dims(x,1)
    elif rank == 3:
        xy = np.stack([x,y]).transpose()

    # create linear regression object
    mlr = LinearRegression()

    chisqs = []
    coeffs = []
    while True:
        # fit linear regression
        mlr.fit(xy, z, sample_weight=w)

        r = np.abs(z - mlr.predict(xy))
        chisq = np.sum((r**2*w))/(z.size-3)
        chisqs.append(chisq)
        k = 1.5 * MAD_NORMALIZE * np.median(r)
        w = np.where(r <= k, 1, (2*k/r) - (k * k/(r**2)))
        sig = 1 if len(chisqs)==1 else gmtstat_f_q(chisqs[-1], z.size-3, chisqs[-2], z.size-3)
        # Go back to previous model only if previous chisq < current chisq
        if len(chisqs)==1 or chisqs[-2] > chisqs[-1]:
            coeffs = [mlr.intercept_, *mlr.coef_]

        #print ('chisq', chisq, 'significant', sig)
        if sig < sig_threshold:
            break

    # get the slope and intercept of the line best fit
    return (coeffs[:rank])

def calculate_mse(data, coeffs, rank):
    z_actual = data[:, 2]

    if rank == 1:
        z_predicted = coeffs[0]
    elif rank == 2:
        # Interpolate x the same way as in GMT_trend2d
        x = data[:, 0]
        x_interp = np.interp(x, (x.min(), x.max()), (-1, +1))
        z_predicted = coeffs[0] + coeffs[1] * x_interp
    elif rank == 3:
        # Interpolate x and y the same way as in GMT_trend2d
        x = data[:, 0]
        x_interp = np.interp(x, (x.min(), x.max()), (-1, +1))
        y = data[:, 1]
        y_interp = np.interp(y, (y.min(), y.max()), (-1, +1))
        z_predicted = coeffs[0] + coeffs[1] * x_interp + coeffs[2] * y_interp

    mse = np.mean((z_actual - z_predicted) ** 2)
    return mse

def test_mse(num_points = 100*1000, ranks = [1, 2, 3], noise_levels = [0, 1, 10, 50]):
    import warnings

    results = []
    # Suppress the specific warning
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=RuntimeWarning)
        for rank in ranks:
            for noise_level in noise_levels:
                data = generate_data(rank, num_points, noise_level)
                # round the output
                coeffs_gmt = [v.round(8) for v in GMT_trend2d(data, rank)]

                mse_gmt = np.round(calculate_mse(data, coeffs_gmt, rank), 0)

                results.append([rank, noise_level, mse_gmt])

    headers = ["Rank", "Noise Level", "GMT_trend2d, MSE"]
    print(tabulate(results, headers=headers))

test_mse()

Код из библиотеки

Скрытый текст
using namespace np;
using namespace scipy;
using namespace sklearn;

auto generate_data(auto rank, auto num_points, auto noise_level) {
    random::seed(42);
    auto x = linspace(-10.0, 10.0, num_points);
    auto y = linspace(-10.0, 10.0, num_points);
    if (rank == 1) {
        auto z = 3 * x + 5 + noise_level * random::randn(num_points);
        return column_stack(x, y, z);
    }
    if (rank == 2) {
        auto z = 2 * x + 3 * y + 5 + noise_level * random::randn(num_points);
        return column_stack(x, y, z);
    }
    auto z = 2 * x * x + 3 * y * y + 5 + noise_level * random::randn(num_points);
    return column_stack(x, y, z);
}

auto GMT_trend2d(const Array<float_> &data, int rank) {
    float_ MAD_NORMALIZE = 1.4826;
    float_ sig_threshold = 0.51;

    if (rank != 1 && rank != 2 && rank != 3) {
        throw sklearn::RuntimeError("Number of model parameters \"rank\" should be 1, 2, or 3");
    }

    auto gmtstat_f_q = [](float_ chisq1, float_ nu1, float_ chisq2, float_ nu2) {
        if (chisq1 == 0.0) return 1.0;
        if (chisq2 == 0.0) return 0.0;
        return scipy::special::betainc(0.5 * nu2, 0.5 * nu1, chisq2 / (chisq2 + chisq1));
    };

    Array<float_> x;
    if (rank == 2 || rank == 3) {
        auto x_ = data[":,0"];
        x = interp(x_, Array<float_>{x_.min(), x_.max()}, Array<float_>{-1, +1});
    }
    Array<float_> y;
    if (rank == 3) {
        auto y_ = data[":,1"];
        y = interp(y_, Array<float_>{y_.min(), y_.max()}, Array<float_>{-1, +1});
    }
    auto z = data[":, 2"].copy();
    Array<float_> w = ones(z.shape()).copy();

    Array<float_> xy;
    if (rank == 1) {
        xy = expand_dims(zeros(z.shape()), 1);
    } else if (rank == 2) {
        xy = expand_dims(x, 1);
    } else if (rank == 3) {
        xy = stack(x, y).transpose();
    }

    auto mlr = linear_model::LinearRegression{};

    std::vector<float_> chisqs;
    Array<float_> coeffs;

    while (true) {
        mlr.fit(xy, z, w);

        auto r = abs_sub(z, mlr.predict(xy));
        auto chisq = sum_sq_weighted(r, w) / static_cast<float_>(z.size() - 3);
        chisqs.push_back(chisq);

        auto k = 1.5 * MAD_NORMALIZE * median(r);
        w = where_tukey(r, k);
        auto sig = (chisqs.size() == 1 ? 1 : gmtstat_f_q(chisqs[chisqs.size() - 1], static_cast<float_>(z.size() - 3), chisqs[chisqs.size() - 2], static_cast<float_>(z.size() - 3)));
        if (chisqs.size() == 1 or chisqs[chisqs.size() - 2] > chisqs[chisqs.size() - 1]) {
            coeffs = mlr.coeffs_();
        }

        if (sig < sig_threshold) {
            break;
        }
    }
    auto result = Array<float_>{};
    for (int i = 0; i < rank; ++i) {
        result = append(result, Array<float_>{coeffs.get(i)});
    }
    return result;

auto calculate_mse(const Array<float_> &data, const Array<float_> &coeffs, int rank) {
    auto z_actual = data[":,2"].copy();
    Array<float_> z_predicted;

    if (rank == 1) {
        z_predicted = coeffs.get(0) * ones(z_actual.shape());
    } else if (rank == 2) {
        // Interpolate x the same way as in GMT_trend2d
        auto x_ = data[":,0"];
        auto x = interp(x_, Array<float_>{x_.min(), x_.max()}, Array<float_>{-1, +1});
        z_predicted = coeffs.get(0) + coeffs.get(1) * x;
    } else if (rank == 3) {
        // Interpolate x and y the same way as in GMT_trend2d
        auto x_ = data[":,0"];
        auto x = interp(x_, Array<float_>{x_.min(), x_.max()}, Array<float_>{-1, +1});
        auto y_ = data[":,1"];
        auto y = interp(y_, Array<float_>{y_.min(), y_.max()}, Array<float_>{-1, +1});
        z_predicted = coeffs.get(0) + coeffs.get(1) * x + coeffs.get(2) * y;
    }
    using sklearn::metrics::mean_squared_error;
    using sklearn::metrics::MeanSquaredErrorParameters;
    MeanSquaredErrorParameters<Array<float_>> params{.y_true = z_actual, .y_pred = z_predicted};
    return mean_squared_error(params);
}

void test_mse(int num_points = 100 * 1000, const std::vector<int> &ranks = {1, 2, 3}, const std::vector<int> &noise_levels = {0, 1, 10, 50}) {
    std::vector<std::tuple<int, int, float_>> results;
    for (auto rank: ranks) {
        for (auto noise_level: noise_levels) {
            auto data = generate_data(rank, num_points, noise_level);
            auto coeffs_gmt = GMT_trend2d(data, rank);
            // round coefficients to 8 decimal places
            auto coeffs_rounded = coeffs_gmt.copy();
            for (std::size_t i = 0; i < coeffs_rounded.size(); ++i) {
                coeffs_rounded.set(i, std::round(coeffs_rounded.get(i) * 1e8) / 1e8);
            }
            auto mse_gmt = calculate_mse(data, coeffs_rounded, rank);
            // round MSE to zero decimal places
            mse_gmt = std::round(mse_gmt);
            results.emplace_back(rank, noise_level, mse_gmt);
        }
    }
    // print table
    std::cout << "Rank\tNoise Level\tGMT_trend2d, MSE\n";
    for (const auto &[rank, noise_level, mse]: results) {
        std::cout << rank << "\t" << noise_level << "\t" << mse << "\n";
    }
}

Результаты бенчмарка на AVX-2

Rank

Noise Level

C++ Time [ms]

Python Time [ms]

Speedup (C++ vs Py)

Result

1

0

6.623

12.580

1.9x (+90%)

C++ FASTER

1

1

6.326

11.698

1.8x (+85%)

C++ FASTER

1

10

8.351

17.884

2.1x (+114%)

C++ FASTER

1

50

8.423

17.564

2.1x (+109%)

C++ FASTER

2

0

11.848

24.378

2.1x (+106%)

C++ FASTER

2

1

13.988

21.392

1.5x (+53%)

C++ FASTER

2

10

14.298

21.454

1.5x (+50%)

C++ FASTER

2

50

13.892

21.267

1.5x (+53%)

C++ FASTER

3

0

22.118

27.332

1.2x (+24%)

C++ FASTER

3

1

21.651

26.097

1.2x (+21%)

C++ FASTER

3

10

22.267

25.905

1.2x (+16%)

C++ FASTER

3

50

24.563

33.731

1.4x (+37%)

C++ FASTER

Результаты бенчмарка на AVX-512

Rank

Noise Level

C++ Time [ms]

Python Time [ms]

Speedup (C++ vs Py)

Result

1

0

10.465

14.564

1.4x (+39%)

C++ FASTER

1

1

8.728

13.618

1.6x (+56%)

C++ FASTER

1

10

11.995

20.686

1.7x (+72%)

C++ FASTER

1

50

12.432

19.809

1.6x (+59%)

C++ FASTER

2

0

13.804

16.047

1.2x (+16%)

C++ FASTER

2

1

16.994

26.332

1.5x (+55%)

C++ FASTER

2

10

16.816

25.047

1.5x (+49%)

C++ FASTER

2

50

15.862

25.106

1.6x (+58%)

C++ FASTER

3

0

22.385

29.881

1.3x (+33%)

C++ FASTER

3

1

21.063

29.933

1.4x (+42%)

C++ FASTER

3

10

21.285

30.517

1.4x (+43%)

C++ FASTER

3

50

25.981

36.520

1.4x (+41%)

C++ FASTER

Заключение

Таким образом, мы смогли ускорить работу библиотек более чем в два раза и сократить использование памяти примерно в 1.5 раза. Что нас ждет дальше? В планах продолжить развитие темы линейной регрессии, чтобы обеспечить быстрое вычисление на больших массивах данных. Также хотелось бы довести все библиотеки до полного соответствия с API аналогичных библиотек на Python. Кроме того, мы планируем создать новые библиотеки для машинного обучения и реализовать нейронные сети, скажем, аналог PyTorch или Tensorflow.

С помощью вайбкодинга возможности разработчиков становятся поистине безграничными.

Вопросы

  • Я с нетерпением жду обратной связи от сообщества. Интересно, действительно ли эта разработка окажется полезной для пользователей или она останется лишь увлечением для разработчиков, не находя применения в широких кругах?

  • Есть ли предложения по дальнейшему развитию этих библиотек? Возможно, кому-то нужно ускорить работу в Jupyter и подготовить его в виде компактного и быстрого исполняемого файла для продакшена? Размер бинарника из примера составляет всего около 2 Мб. Не стесняйтесь обращаться!

  • Какое направление, по вашему мнению, следует развивать более активно? Возможно, это будет numpy, pandas, scipy, sklearn, или стоит сосредоточиться на нейросетях?

  • Также приглашаю желающих помочь протестировать библиотеки на процессорах AMX. У меня нет доступа к таким процессорам, но подойдут машины с CPU 4-го поколения Intel Xeon Scalable (Sapphire Rapids), 5-го поколения Intel Xeon Scalable (Emerald Rapids) или процессорами Intel Xeon 6 (Granite Rapids / Sierra Forest).

  • Ищем помощь для настройки сборки и тестирования библиотек под Windows. У меня есть компьютер с этой ОС, но я давно не проверял его, и, скорее всего, сборка сейчас не работает.

  • Если кто-то хочет поучаствовать в разработке и внести свой вклад, буду рад вашему отклику! Пишите мне в личные сообщения. Спасибо!

Ссылки