ПроектыФорумСобираю команду

Сетевой движок ММОРПГ (8 стр)

Страницы: 17 8 9 1020 Следующая »
#105
7:32, 20 июня 2025

NicoAZ
> Попытка что-то начать доказывать - это изначально было ошибкой с моей стороны.
Не понятно зачем доказывать, когда можно написать релевантный нагрузочный тест и измерить. Очевидно, нерелевантный тест будет измерять свое качество а не интересующую нас производительность.

#106
7:35, 20 июня 2025

Вий
> Не понятно зачем доказывать, когда можно написать релевантный нагрузочный тест и измерить. Очевидно, нерелевантный тест будет измерять свое качество а не интересующую нас производительность.
Как я уже сказал, я умываю руки. Вы можете продолжать спор, если интересно.

#107
(Правка: 8:27) 7:59, 20 июня 2025

NicoAZ
> Вы можете продолжать спор, если интересно.
Какой спор то? О чем?

NicoAZ
> Как я уже сказал, я умываю руки
Ну вот, сначала сам спросил, что не так с тестом, а когда ему рассказали, как его оптимизировать, ускорив раз в 8, сбежал. Хотя сначала вроде хотел попробовать себя в разработке высокопроизводительных игровых серверов.

Вечером я запущу и первоначальный вариант и улучшенный и покажу результаты. Вечер у меня часов через 8

#108
22:14, 20 июня 2025

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

#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>
#include <cstdint>
#include <string>
#include <memory>

#define STR1_MAX_LEN 20
#define STR2_MAX_LEN 30
#define NUM_STRUCTURES 100000
#define NUM_ITERATIONS 100000

// Сырая структурка
struct MyData {
    char string1[STR1_MAX_LEN];
    char string2[STR2_MAX_LEN];
    int int1;
    int int2;
    int int3;
};

// Структурка, которую шлем
struct EnhancedData {
    MyData   payload;           // Сырая
    uint32_t payload_hash;      // Хеш сырой
    uint64_t generation_time;   // Время создания
};

// Хешируем. Не продакшн риди, открыт для предложений.
uint32_t calculate_data_hash(const MyData* data) {
    uint32_t hash = 1234;
    const auto* ptr = reinterpret_cast<const unsigned char*>(data);
    size_t len = sizeof(MyData);

    for (size_t i = 0; i < len; ++i) {
        hash = ((hash << 5) + hash) + ptr[i];
    }
    return hash;
}

// Полная функция сериализации (включает подготовку данных + сериализацию)
size_t serialize_data_for_transmission(const MyData* original_data, unsigned char* buffer, size_t buffer_size) {
    constexpr size_t required_size = sizeof(EnhancedData);
    if (buffer_size < required_size) {
        return 0;
    }

    // Подготавливаем EnhancedData
    EnhancedData enhanced;
    enhanced.payload = *original_data;
    enhanced.payload_hash = calculate_data_hash(original_data);

    auto now = std::chrono::system_clock::now();
    auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
    enhanced.generation_time = static_cast<uint64_t>(timestamp);

    // Сериализуем в буфер
    std::memcpy(buffer, &enhanced, required_size);
    return required_size;
}

int main() {
    std::cout << "Выделение памяти для " << NUM_STRUCTURES << " структур...\n";

    // Выделяем память для массива структур
    auto data_array = std::make_unique<MyData[]>(NUM_STRUCTURES);

    // Заполняем массив разными данными
    for (int i = 0; i < NUM_STRUCTURES; i++) {
        std::memset(&data_array[i], 0, sizeof(MyData));

        std::snprintf(data_array[i].string1, STR1_MAX_LEN, "String_%d", i);
        std::snprintf(data_array[i].string2, STR2_MAX_LEN, "Data_%d_important", i);

        data_array[i].int1 = i + 228;
        data_array[i].int2 = -666 - i;
        data_array[i].int3 = 1488 + i * 2;
    }

    // Выделяем буфер для сериализации
    auto serialize_buffer = std::make_unique<unsigned char[]>(sizeof(EnhancedData));

    std::cout << "Начинаем тест сериализации " << NUM_ITERATIONS << " операций...\n";

    // Переменные для предотвращения оптимизации
    uint64_t checksum = 0;
    size_t total_bytes = 0;

    // Тест полной сериализации (подготовка + хеширование + сериализация)
    auto start_time = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < NUM_ITERATIONS; i++) {
        // Используем разные структуры из массива
        int data_index = i % NUM_STRUCTURES;
        size_t serialized_size = serialize_data_for_transmission(&data_array[data_index],
                                                               serialize_buffer.get(),
                                                               sizeof(EnhancedData));

        // Используем результат сериализации чтобы предотвратить оптимизацию
        total_bytes += serialized_size;

        // Добавляем несколько байт из буфера в контрольную сумму
        checksum += serialize_buffer[0] + serialize_buffer[serialized_size/2] + serialize_buffer[serialized_size-1];
    }

    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);

    // Результаты
    std::cout << "Время: " << duration.count() << " мкс ("
              << static_cast<double>(duration.count()) / 1000.0 << " мс)\n";
    std::cout << "Среднее время: "
              << static_cast<double>(duration.count()) / NUM_ITERATIONS << " мкс\n";
    std::cout << "Скорость: "
              << static_cast<double>(NUM_ITERATIONS) * 1000000.0 / duration.count() << " оп/сек\n";

    // Выводим данные чтобы оптимизатор не удалил вычисления
    std::cout << "Контрольная сумма: " << checksum
              << ", общий объем: " << total_bytes << " байт\n";

    return 0;
}

output

Выделение памяти для 100000 структур...
Начинаем тест сериализации 100000 операций...
Время: 16992 мкс (16.992 мс)
Среднее время: 0.16992 мкс
Скорость: 5.88512e+06 оп/сек
Контрольная сумма: 8300000, общий объем: 8000000 байт
#109
22:30, 20 июня 2025

Вий
> Время: 16992 мкс (16.992 мс)

Ну отлично, да. Это чисто замаршаллить структурку с подсчётом хеша. Просто великолепно. Могу лишь в очередной раз успехов пожелать. :)

#110
22:32, 20 июня 2025

А вот я сделал самую крошечную оптимизацию функции хеширования:

#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>
#include <cstdint>
#include <string>
#include <memory>

#define STR1_MAX_LEN 20
#define STR2_MAX_LEN 32
#define NUM_STRUCTURES 100000
#define NUM_ITERATIONS 100000

// Сырая структурка
struct MyData {
    int int1;
    int int2;
    int int3;
    char string1[STR1_MAX_LEN];
    char string2[STR2_MAX_LEN];
};

// Структурка, которую шлем
struct EnhancedData {
    MyData   payload;           // Сырая
    uint32_t payload_hash;      // Хеш сырой
    uint64_t generation_time;   // Время создания
};

// Хешируем.
uint32_t calculate_data_hash(const MyData* data) {
    uint64_t hash = 1234;
    size_t len = sizeof(MyData);
    for (size_t i = 0; i < len; i+=8) {
  uint64_t tmp;
  memcpy(&tmp, (char*)data + i, 8);
        hash = ((hash << 5) + hash) + tmp;
    }
    return (uint32_t)(hash ^ (hash >> 32));
}

// Полная функция сериализации (включает подготовку данных + сериализацию)
size_t serialize_data_for_transmission(const MyData* original_data, unsigned char* buffer, size_t buffer_size) {
    constexpr size_t required_size = sizeof(EnhancedData);
    if (buffer_size < required_size) {
        return 0;
    }

    // Подготавливаем EnhancedData
    EnhancedData enhanced;
    enhanced.payload = *original_data;
    enhanced.payload_hash = calculate_data_hash(original_data);

    auto now = std::chrono::system_clock::now();
    auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
    enhanced.generation_time = static_cast<uint64_t>(timestamp);

    // Сериализуем в буфер
    std::memcpy(buffer, &enhanced, required_size);
    return required_size;
}

int main() {
    std::cout << "Выделение памяти для " << NUM_STRUCTURES << " структур...\n";

    // Выделяем память для массива структур
    auto data_array = std::make_unique<MyData[]>(NUM_STRUCTURES);

    // Заполняем массив разными данными
    for (int i = 0; i < NUM_STRUCTURES; i++) {
        std::memset(&data_array[i], 0, sizeof(MyData));

        std::snprintf(data_array[i].string1, STR1_MAX_LEN, "String_%d", i);
        std::snprintf(data_array[i].string2, STR2_MAX_LEN, "Data_%d_important", i);

        data_array[i].int1 = i + 228;
        data_array[i].int2 = -666 - i;
        data_array[i].int3 = 1488 + i * 2;
    }

    // Выделяем буфер для сериализации
    auto serialize_buffer = std::make_unique<unsigned char[]>(sizeof(EnhancedData));

    std::cout << "Начинаем тест сериализации " << NUM_ITERATIONS << " операций...\n";

    // Переменные для предотвращения оптимизации
    uint64_t checksum = 0;
    size_t total_bytes = 0;

    // Тест полной сериализации (подготовка + хеширование + сериализация)
    auto start_time = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < NUM_ITERATIONS; i++) {
        // Используем разные структуры из массива
        int data_index = i % NUM_STRUCTURES;
        size_t serialized_size = serialize_data_for_transmission(&data_array[data_index],
                                                               serialize_buffer.get(),
                                                               sizeof(EnhancedData));

        // Используем результат сериализации чтобы предотвратить оптимизацию
        total_bytes += serialized_size;

        // Добавляем несколько байт из буфера в контрольную сумму
        checksum += serialize_buffer[0] + serialize_buffer[serialized_size/2] + serialize_buffer[serialized_size-1];
    }

    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);

    // Результаты
    std::cout << "Время: " << duration.count() << " мкс ("
              << static_cast<double>(duration.count()) / 1000.0 << " мс)\n";
    std::cout << "Среднее время: "
              << static_cast<double>(duration.count()) / NUM_ITERATIONS << " мкс\n";
    std::cout << "Скорость: "
              << static_cast<double>(NUM_ITERATIONS) * 1000000.0 / duration.count() << " оп/сек\n";

    // Выводим данные чтобы оптимизатор не удалил вычисления
    std::cout << "Контрольная сумма: " << checksum
              << ", общий объем: " << total_bytes << " байт\n";

    return 0;
}

и вышло вот так вот

Выделение памяти для 100000 структур...
Начинаем тест сериализации 100000 операций...
Время: 6932 мкс (6.932 мс)
Среднее время: 0.06932 мкс
Скорость: 1.44259e+07 оп/сек
Контрольная сумма: 18038548, общий объем: 8000000 байт

стало быстрее в 2 раза, 69 наносекунд на запись на raspberry pi 4

#111
22:38, 20 июня 2025

NicoAZ
> Ну отлично, да.
Да вроде неплохо, 8 мегабайт за 7 мс, это 1.14 гигабайта в секунду.
Сеть, напомню, пропустить может только 1 гигабит в секунду, а в гигабайте их целвых 8.

#112
22:51, 20 июня 2025

Вий
> Да вроде неплохо, 8 мегабайт за 7 мс, это 1.14 гигабайта в секунду.
> Сеть, напомню, пропустить может только 1 гигабит в секунду, а в гигабайте их целвых 8.
Ну я ж и говорю, если задача сервера - брать данные из переменной, хешировать и отправлять - мощности хватит. Вы попробуйте что-то помимо этого добавить. Ну, например, начать можете с того, что не просто из переменной берите статические данные, а каждый раз рандомные инты генерируйте...  Это, конечно, даже близко не то, что реальный игровой сервер будет делать, но хоть что-то уже...

#113
(Правка: 23:24) 23:08, 20 июня 2025

А если исправить в коде еще пару явных косяков произвоидительности

#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>
#include <cstdint>
#include <string>
#include <memory>

#define STR1_MAX_LEN 20
#define STR2_MAX_LEN 32
#define NUM_STRUCTURES 100000
#define NUM_ITERATIONS 100000

// Сырая структурка
struct MyData {
    int int1;
    int int2;
    int int3;
    char string1[STR1_MAX_LEN];
    char string2[STR2_MAX_LEN];
};

struct Footer {
    uint32_t payload_hash;      // Хеш сырой
    uint64_t generation_time;   // Время создания
};

// Структурка, которую шлем
struct EnhancedData {
    MyData   payload;           // Сырая
    Footer footer;
};

// Хешируем.
uint32_t calculate_data_hash(const MyData* data) {
    uint64_t hash = 1234;
    size_t len = sizeof(MyData);
    for (size_t i = 0; i < len; i+=8) {
        uint64_t tmp;
        memcpy(&tmp, (char*)data + i, 8);
        hash = ((hash << 5) + hash) + tmp;
    }
    return (uint32_t)(hash ^ (hash >> 32));
}

// Полная функция сериализации (включает подготовку данных + сериализацию)
size_t serialize_data_for_transmission(const MyData* original_data, unsigned char* buffer, size_t buffer_size, uint64_t ts) {
    constexpr size_t required_size = sizeof(EnhancedData);
    if (buffer_size < required_size) {
        return 0;
    }

    // Подготавливаем EnhancedData
    Footer enhanced;
    enhanced.payload_hash = calculate_data_hash(original_data);


    enhanced.generation_time = ts;

    // Сериализуем в буфер
    std::memcpy(buffer, original_data, sizeof(MyData)); 
    std::memcpy(buffer + sizeof(MyData), &enhanced, sizeof(Footer));
    return required_size;
}

int main() {
    std::cout << "Выделение памяти для " << NUM_STRUCTURES << " структур...\n";

    // Выделяем память для массива структур
    auto data_array = std::make_unique<MyData[]>(NUM_STRUCTURES);

    // Заполняем массив разными данными
    for (int i = 0; i < NUM_STRUCTURES; i++) {
        std::memset(&data_array[i], 0, sizeof(MyData));

        std::snprintf(data_array[i].string1, STR1_MAX_LEN, "String_%d", i);
        std::snprintf(data_array[i].string2, STR2_MAX_LEN, "Data_%d_important", i);

        data_array[i].int1 = i + 228;
        data_array[i].int2 = -666 - i;
        data_array[i].int3 = 1488 + i * 2;
    }

    // Выделяем буфер для сериализации
    auto serialize_buffer = std::make_unique<unsigned char[]>(sizeof(EnhancedData));

    std::cout << "Начинаем тест сериализации " << NUM_ITERATIONS << " операций...\n";

    // Переменные для предотвращения оптимизации
    uint64_t checksum = 0;
    size_t total_bytes = 0;

    // Тест полной сериализации (подготовка + хеширование + сериализация)
    auto start_time = std::chrono::high_resolution_clock::now();


    auto now = std::chrono::system_clock::now();
    auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
    uint64_t ts = static_cast<uint64_t>(timestamp);

    for (int i = 0; i < NUM_ITERATIONS; i++) {
        // Используем разные структуры из массива
        int data_index = i % NUM_STRUCTURES;
        size_t serialized_size = serialize_data_for_transmission(&data_array[data_index],
                                                               serialize_buffer.get(),
                                                               sizeof(EnhancedData), ts);

        // Используем результат сериализации чтобы предотвратить оптимизацию
        total_bytes += serialized_size;

        // Добавляем несколько байт из буфера в контрольную сумму
        checksum += serialize_buffer[0] + serialize_buffer[serialized_size/2] + serialize_buffer[serialized_size-1];
    }

    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);

    // Результаты
    std::cout << "Время: " << duration.count() << " мкс ("
              << static_cast<double>(duration.count()) / 1000.0 << " мс)\n";
    std::cout << "Среднее время: "
              << static_cast<double>(duration.count()) / NUM_ITERATIONS << " мкс\n";
    std::cout << "Скорость: "
              << static_cast<double>(NUM_ITERATIONS) * 1000000.0 / duration.count() << " оп/сек\n";

    // Выводим данные чтобы оптимизатор не удалил вычисления
    std::cout << "Контрольная сумма: " << checksum
              << ", общий объем: " << total_bytes << " байт\n";

    return 0;
}

То получается

Выделение памяти для 100000 структур...
Начинаем тест сериализации 100000 операций...
Время: 2098 мкс (2.098 мс)
Среднее время: 0.02098 мкс
Скорость: 4.76644e+07 оп/сек
Контрольная сумма: 18050942, общий объем: 8000000 байт

а это уже 21 наносекунда на запись
или 3.8 гигабайта в секунду

Это примерно в 10 раз быстрее, чем предложенный тобой код. Вот поэтому я и говорю, что найти крутых программистов тяжело. Но ничего, я уже привык.

Сколько я там обещал гигабайт в секунду у raspberry pi, напомни?

#114
(Правка: 23:13) 23:10, 20 июня 2025

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

#115
23:58, 20 июня 2025

вместо генерации случайных чисел можно добавить 100 тыс наборов чисел:пары координат x,y ,  HP,MP,EP,ARM, и ещё 50 - они будут имитировать параметры персонажей +надетые вещи+эффекты на персонаже, и меняй их на 1...100 два раза в секунду. сколько займет времени? поменять 10 лямов чисел в секунду, как повлияет на твои 0.021 мкс?

#116
2:54, 21 июня 2025

Airumi
Как Вы могли заметить, у нас тут такой случай, когда информацию надо очень дозировано подавать. :) То время, что он получил - это вообще время маршаллинга одной структурки в другую. До того, что первая структурка должна откуда-то появиться, а вторая - куда-то отправиться, это мы так далеко пока даже не заходили. :)

#117
(Правка: 3:35) 3:16, 21 июня 2025

NicoAZ
> То время, что он получил - это вообще время маршаллинга одной структурки в другую.

Как странно, ты ведь до этого считал что и это невозможно сделать достаточно быстро:

NicoAZ
> Давайте я Вам прям сегодня ради интереса напишу программку на Сях, которая будет просто подгружать какой нибудь массив структурок, а потом эти структуру маршаллить в минимально пригодный для передачи формат. Даже отправлять ничего никуда не будет, чисто маршаллинг. Там посмотрите, сколько будет один пакет ресурсов жрать чисто на создание, без какой либо бизнес-логики, без отправки, без ничего. Дайте знать, если интересно - займусь.
> Мне реально интересно было бы конкретные идеи по этому поводу послушать. Как и чего конкретно Вы реализуете, чтобы обойти описанные мной грабли.

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

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

#118
(Правка: 3:49) 3:47, 21 июня 2025

Вий
> Как странно, ты ведь до этого считал что и это невозможно сделать достаточно быстро
Ну так и не возможно.

Вий
> Я показал что граблей просто нет, пишешь высокопроизводительный код и все работает.
Что работает? Маршаллинг одной структуры в другую, даже без передачи? Работает, вопросов нет. Знаете, какой процент от общего количества потребляемых ресурсов этот процесс вообще обычно занимает?

Вий
> Боюсь, проблема не в задаче, а в том, что вы не умеете писать высокопроизводительный код. И вместо того чтобы учиться, пытаетесь доказать что учиться нечему. Это печально.
Мы Ваш код обсуждаем. Где совершенно очевидно, что 100 000 онлайна на RPi 4 держать не получится прям вот вообще никак, даже не близко, даже с задержкой риакции на действие в несколько секунд.

#119
3:47, 21 июня 2025

Airumi
> вместо генерации случайных чисел можно добавить 100 тыс наборов чисел:пары координат x,y ,  HP,MP,EP,ARM, и ещё 50 - они будут имитировать параметры персонажей +надетые вещи+эффекты на персонаже, и меняй их на 1...100 два раза в секунду. сколько займет времени?

Примерно столько же, сколько занимает сложение в функции хеширования. Там ведь сейчас делается по сути то что тебя интересует на скорости 3.8 гигабайта в секунду

Страницы: 17 8 9 1020 Следующая »
ПроектыФорумСобираю команду