Самый быстрый Индиан

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

Легендарный супероптимизированый алгоритм, который реализует весьма сложные Trie структуры данных и топчет вот эти вот эти ваши самые реализации ассоциативных массивов std::map, glib, dictionary.net и прочью заморскую попсу в разы, а то и на порядок.

github.com/Bazist/HArray (Windows & Linux)

PS: Готовлю потихоньку раскат гром и молнии на хабр, но пока потренируемся на Доу.

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось1
До обраногоВ обраному3
LinkedIn

Найкращі коментарі пропустити

1. Не надо «the most optimized». Просто «Trie implementation in C++», или «Fast Trie implementation». Но лучше без «fast», если нет тестов и легко воспроизводимых benchmark’ов.
2. Tab for indentation, spaces for alignment. Можно забить, и получить срач на тему spaces-vs-tabs вместо обсуждения кода когда оно пойдёт на Reddit.
github.com/...ValuesByRange.cpp#L22-L29
3. Не надо оставлять закомментированные блоки. Если один лишний повод для срача.
github.com/...dKeyAndValue.cpp#L22-L204
4. StdAfx.h, windows.h, int _tmain(int, _TCHAR*[]) — wtf? system("pause")?!
5. printf’ы в C++ коде? После include <iostream>? Ещё один повод для срача.

6*. Не эксперт в VS, но я бы ожидал там два проекта: собственно библиотеку, и отдельно от неё демку/тест/пример который main.c.
7*. Зачем в .gitattributes строки с 3-й до конца? Зачем мусор в .gitignore? VS реально столько оставляет в проекте, или это просто копипаст откуда-то?

8. Обычные Makefile-ы сильно увеличат шансы сборки левыми людьми.
Соответственно шансы получить отзывы по сути, а не по форматированию.
Или CMakeLists, и поубирать VS проекты.

9. Тесты, или что-то хотя бы отдалённо похожее на тесты. Сильно рекомендуется. Очень сильно.

Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Решил установить harray. Вот что делал согласно readme.mod for Win10

C:\Users\1>cmake --version
cmake version 3.21.1
CMake suite maintained and supported by Kitware (kitware.com/cmake).
C:\Users\1\home\c++>cd harray
C:\Users\1\home\c++\harray>mkdir build
C:\Users\1\home\c++\harray>cmake -Bbuild
-- Building for: NMake Makefiles
CMake Error at CMakeLists.txt:3 (project):
  Running
   'nmake' '-?'
  failed with:
   Не удается найти указанный файл
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!
See also "C:/Users/1/home/c++/harray/build/CMakeFiles/CMakeOutput.log".
C:\Users\1\home\c++\harray>

Как это исправить?

Здесь пишут решение
stackoverflow.com/...​compiler-and-cxx-compiler

Но под Windows все проще. Если у тебя установлена студия то просто клонируешь репозиторий и открываешь готовый солюшин

github.com/...​r/HArray_VisualStudio.sln

Я сделал так только что: скачал с гитхаба и создал папку harray и содержимое zip забросил внутрь, затем «открыть с помощью vscode» на sln файле.
Что делать дальше?

Попробовать сбилдить + запустить

А что писать в байлд таске? (tasks.json)

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "msbuild",
            "args": [
                // Ask msbuild to generate full paths for file names.
                "/property:GenerateFullPaths=true",
                "/t:build",
                // Do not generate summary otherwise it leads to duplicate errors in Problems panel
                "/consoleloggerparameters:NoSummary"
            ],
            "group": "build",
            "presentation": {
                // Reveal the output only if unrecognized errors occur.
                "reveal": "silent"
            },
            // Use the standard MS compiler pattern to detect errors, warnings and infos
            "problemMatcher": "$msCompile"
        }
    ]
}

Хм, первый раз сталкиваюсь.
У себя ставил обычную бесплатную VS Community
visualstudio.microsoft.com/ru/vs/community

Может завтра будет время поставлю VS Code посмотрю подробнее как под ней запустить.

Кул. А видео процесса можешь записать? VSDC пишет неплохо. Я им пользуюсь.
У тебя ведь *nix? Вдруг не подойдет винде? В общем, пойду я тоже гуглить ошибку.
Посмотрим чего найду.

У тебе не налаштований PATH до компілятора. Спробуй те саме запустити у VS Developer Console чи як там у тебе воно називається (шукай в Start->Visual Studio...). Або на сайті МС де качав dev tools читай як налаштовувати.

У меня есть mingw, он может компилировать.
Осталось написать bat чтобы:

cmake_minimum_required(VERSION 3.10)
project(HArray LANGUAGES CXX)
# set(CMAKE_CXX_STANDARD 11)
add_library(harray STATIC
  src/HArray_delValueByKey.cpp
  src/HArray_getKeysAndValuesByRange.cpp
  src/HArray_getValueByKey.cpp
  src/HArray_hasPartKey.cpp
  src/HArray_insert.cpp
  src/HArray_insertOrGet.cpp
  src/HArray_rebuild.cpp
  src/HArray_scanKeysAndValues.cpp
  src/HArray_shrink.cpp
  src/HArray_test.cpp
  src/HArrayGeneric.cpp)
target_include_directories(harray PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/harray)
add_executable(HArrayBenchmark benchmark/HArrayBenchmark.cpp)
target_link_libraries(HArrayBenchmark harray)

Чтобы тот учитывал все вышеприведенное.
А все эти более сложные штуки cmake, nmake соответственно накапливают все больше и больше несоответствий версий и других нестыковок.
Когда я встречаю такое, я чаще всего ищу более абстрактное решение и пытаюсь запустить его.

Ніякі bat-файли не потрібні, cmake для того і придумали, щоб не писати для кожної платформи на компілятору скріпти.

Твій mingw скоріше за все не прописаний у PATH і cmake через те його не бачить і намагається використовувати майкрософтовські тулзи — звідти і виклик nmake.

Почитай документацію і налаштуй свій mingw для початку — sourceforge.net/...​GeneralUsageInstructions

Вот наше решение:

C:\Users\1\home\c++\harray>cmake -Bbuild2 -G "MinGW Makefiles"
-- The CXX compiler identification is GNU 10.3.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/mingw64/bin/g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Users/1/home/c++/harray/build2
C:\Users\1\home\c++\harray>

Кто-то как залезет в код, как вытянет алгоритм, как запатентует его...

5 лет уж прошло, а Германа все нет ...
На самом деле, самая большая ценность начинается,
когда делается грамотный тюнинг уже под конкретную задачу.
Как начал в свое время выступление Matt Kulukundis, мы думали что key/value это простая задача, но мы провалились в кротовую нору с 100500 ньюансов.
На самом деле это как устройство клетки в организме, все основные фокусы начинаются на уровне микромира. А владелецей этой клетки, ну может быть какая-то муха, комар или грибок. Вообщем ничего интересного.

з цікавості аж я поліз подивитися, але чогось не заводиться. поясніть хтось де я туплю і як цим добром правильно користуватися. я з рідмі забрав вставку-читання:

int main()
{
  HArray ha;
  ha.init();
    
  uint32 key1[] = { 10 };  
  ha.insert(key1, sizeof(key1), 23);
  
  uint32 value;
  uint32 dummy_var[] = { 10 }; 
  if(ha.getValueByKey(key1, sizeof(key1), value))
  {
     printf("value: %d \n", value);
  } else {
     printf("NO KEY \n");
  }

  return 0;
}
компілював під Xubuntu 18, «g++ testharray.cpp -o testharray libharray.a && ./testharray
». на виході постійно NO KEY.

Вот здесь нужно подправить.
Нативно длина ключа в сегментах.
Один сегмент = 4 байта.

ha.insert(key1, sizeof(key1)/4, 23);

дякую, поправив. але результат був такий самий, то я ще в getValue поділив, тоді наче спрацювало.

Да, интерфейс немного специфический. Это для быстродействия.
Есть обертки, например такая. У них уже более привычный интерфейс

HArrayChar ha;
	ha.insert("dou", 3, "hello", 5);
	
	char sayHello[16];
	uint32_t valueLen;

	if (ha.getValueByKey("dou", 3, sayHello, valueLen))
	{
		printf("Key with value %s is found.", sayHello);
	}
	else
	{
		printf("Key is not found.");
	}

	return 0;

скажіть, в чому може бути причина ще такої проблеми: коли я запускаю код

int main()
{
  HArray ha;
  ha.init(10);
  
  uint32 value;
  
  uint32 key1[] = { 10 };
  uint32 key2[] = { 20 };
  
  ha.insert(key1, sizeof(key1)/4, 1);
  //ha.insert(key2, sizeof(key2)/4, 2);
  
  if(ha.getValueByKey(key1, sizeof(key1)/4, value))
  {
     printf("{ 10 } value: %d \n", value);
  } else {
     printf("{ 10 } NO KEY \n");
  }

  return 0;
}
все відпрацьовує добре «{ 10 } value: 1», але варто мені розкоментувати закоментований рядок і видає «{ 10 } NO KEY »?

Хм, вотето поворот. Пофиксил, в мастере есть изменения.
PS: Предистория. Там интерфейс немного менялся недавно

getValueByKey

возвращает true/false. А раньше возвращала сразу value. И не было понятно если вернула 0, это нет ключа или это велью 0. При изменении этого интерфейса был баг, в одном месте забыл проставить return true явно, если значение найдено.

Мені здається тут баг — github.com/...​r/src/HArrayGeneric.h#L64. Має бути не += 2, а *= 2.

Мені здається тут баг — github.com/...​r/src/HArrayGeneric.h#L64. Має бути не += 2, а *= 2.
Точно, пофиксил. Спасибо.

Сорри, не увидел что уже пулл реквест на эти изменения создан.
Замержил.

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

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

Це ІМО класичний приклад коли «непотрібні» юніт-тести зловили б таку елементарну опечатку миттєво. От прямо можна студентам показувати для демонстрації того, що код без юніт-тестів не може вважатися якісним аж ніяк.

Ну что господа, похоже нет выбора.
Будем пилить паттерн матчинг для ключей. Нужно искать ключи не только по префиксам но и по окончанию ключа без фулскана. Это понадобится через недели две.
img2.joyreactor.cc/...​удент-машина-2410784.jpeg

Будем пилить паттерн матчинг для ключей

Реализовал поиск ключей в контейнере по постфиксу (не префиксу) за время лучше О(N).
Оказалось, это было не сложно, но сделано через малоочевидный костыль в юае, сейчас думаю как обернуть в нормальный юай.

Конечно это крутая функциональность, которая открывает дверь в мир версионных кей велью стореджей. Тоесть в мир офлайновых систем которые умеют мержится, распределенных транзакций и много чего еще. Ведь если добавить в конец ключа его версию, то можно быстро находить ключи нужной версии в контейнере, делать срез (снапшот) кей велью контейнера на определенный момент времени (Way Back Machine). Причем в HArray оверхед для отслеживания историчности изменений достаточно небольшой. И вот теперь можно быстро получать срез состояния контейнера на какойто момент времени, благодаря скану ключей по постфиксным сегментам.

PS В честь этого события предлагаю прослушать композицию Hans Zimmer — Time.
youtu.be/RxabLA7UQ9k
Друзья, мы стали ещё чуть ближе к совершенным системам.

Запили биткоин-про и сруби бабла.
P.S. Музычка приятная.

Дауж, харрей не перестает удивлять. Оказывается сканировать ключи в контейнере можно не только по префиксам или по диапазону ключей от и до, как в обычном трай, но и по шаблону. Например найти все ключи которые заканчиваются на XXX за время существенно быстрее O(N) хоть и не O(1) или O(x) где х размер диапазона.
Это вынос мозга, но это факт. Такой скан за время быстрее О(N) абсолютно невозможен не в хештаблицах, не в сортированных бинарных деревьях. А здесь может быть. Это было неочевидно.

Что такое «существенно быстрее О(n)»? Точная оценка есть?

Зависит от природы ключей.
Бест кейс будет примерно таким. Если К это длина ключа, константа, то
О(K) + (O(1) или O(M)), где М всегда не более 8 при любом количестве ключей. Что по нотации большого О всеравно считается О(1).

Такой кейс возможен для пальмовых деревьев. Что такое пальма ? Это дерево которое слабо ветвится в своих корнях, но сильно ветвится в своей короне.

Ворст кейс, будет приближатся к О(N) но всегда будет его чуть-чуть меньше. Ворст кейс можно добится когда у нас дерево типа бамбук. Тоесть ключей много и они все имеют свой индивидуальный корень.

Если написал слишком сумбурно, есть простой пример. Допустим нам нужно искать людей по Имя/Фамилия.
Мы можем сформировать все ключи как [Имя][Фамилия] и получим пальму. Имен мало уникальных, фамилий много уникальных. Поэтому это дерево будет мало ветвится в своем корне и сильно ветвится в своей короне.
В другом варианте, мы можем сформировать все ключи как [Фамилия][Имя]. Тогда получим бамбук. Уникальных фамилий много и дерево в самом начале будет сильно ветвится и делится на индивидуальные иерархии.

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

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

Ворст кейс, будет приближатся к О(N) но всегда будет его чуть-чуть меньше

0.5* O(n) = 5*O(n) = O(n)

0.5* O(n) = 5*O(n) = O(n)

Есть комплексити и О(n*n) и О(n/2).
Если тебе в худшем случае нужно меньше/больше обойти чем N, то значение в скобках может изменятся как в большую так и в меньшую сторону.
www.quora.com/...​2-a-valid-time-complexity

В том и дело что O(чуть меньше n) не бывает. Если это O(n/2) так и пиши. 0.5 * O(n) != O(n/2)

Мое упущение, буду расти :)))

Дуже неочевидний сарказм в пості ІМО, легко сприйняти за серйозний текст.

Профессор по ссылке пишет что формально можно и так.
www.quora.com/...​2-a-valid-time-complexity

В последнем тестовом примере, при поиске ключей по постфиксным сегментам, обошел 8 ключей вместо 2048, которые находились в контейнере. Называть такой скан O(N), сорян, слишком грубо. О(1) назвать тоже не могу, потому что гдето это будет 8 ключей, где-то 20, где-то 100. Но всегда существенно меньше N.

Профессор по ссылке пишет что формально можно и так.

Исходя из того что есть врачи антивакцинаторы, можно одним движением руки вывести утверждение, что есть профессора, для которых O(n) != O(n/8) :-)

В последнем тестовом примере, при поиске ключей по постфиксным сегментам, обошел 8 ключей вместо 2048, которые находились в контейнере. Называть такой скан O(N), сорян, слишком грубо. О(1) назвать тоже не могу, потому что гдето это будет 8 ключей, где-то 20, где-то 100. Но всегда существенно меньше N.

Большое О показывает как быстро растёт количество операций на поиск относительно количества ключей n. Если на 2048 ключей было 100 операций, а на 20480 — 1000, то это все то же O(n). Тебе нужно позапускать контейнер итеративно и посмотреть для разных n, какие будут верхние значения (это O), минимальные и средние (amortized). И вывести это на график.

Большое О показывает как быстро растёт количество операций на поиск относительно количества ключей n. Если на 2048 ключей было 100 операций, а на 20480 — 1000, то это все то же O(n). Тебе нужно позапускать контейнер итеративно и посмотреть для разных n, какие будут верхние значения (это O), минимальные и средние (amortized). И вывести это на график.

Знаете чем специалист отличается от не специалиста. Не специалист видит только белое и черное. А специалист видит еще 100500 оттенков между этими двумя цветами. В данном случае невозможно вывести эту оценку универсально и точно, поскольку она очень сильно зависит не от количества ключей, а от топологии самих ключей формирующих само дерево. Поэтому оценка будет гулять от О(1) best case до О(N) worst case. Примерно как в хештаблицах в зависимости от природы, количества ключей и качества хешфункции.

О(N) worst case

Ты сам себе и ответил.
Твой алгоритм O(n).

Вот сравни свои выкладки с сортировкой:

Пузырёк это O(n*n), но всем понятно что полностью развернутый массив встречается крайне редко и в подавляющем большинстве случаев пузырёк закончит свою работу быстрее. Но от этого его оценка не будет гулять от O(1) до O(n*n). Она так и останется O(n)

А вот не все так однозначно. Тогда бы для _классической_ хештаблицы писали бы оценку О(N) а не О(1). Ведь если сойдутся на небе звезды и все хеши будут бить в одну ячейку, то хештаблице придется сканировать весь сет ключей. Она будет мало чем отличатся от обычного связного списка.
Но вероятность этого настолько мала, что этим можно пренебречь и поставить О(1).

Но вероятность этого настолько мала, что этим можно пренебречь и поставить О(1).

Это big theta, а не O

Для твоего алгоритма и реализации:
Big O = N
Big Theta = 1 (скорее всего 1, если сразу нашла ключ)
Big Omega = где то посредине

представь, что тебя будут дудосить — как в таком случае справится хеш таблица с рандомизированной хеш функцией, и как справится твоя структурка?

Справится хорошо, потому что параметры структуры не зависят от качества хеш функции.
Дудосят ее в бенчмарках, вставляя десятки миллионов ключей разной природы (рандом, последовательные с периодом и тд).

Ні, почитай що таке big O.

А вот не все так однозначно. Тогда бы для _классической_ хештаблицы писали бы оценку О(N) а не О(1).

А никто вменяемый O(1) и не пишет. Пишут про _амортизированное_ O(1) с возможностью получать O(N) в особых случаях. В частности, если по новому ключу производится полное переформатирование таблицы под новый размер, то именно эта вставка будет O(N).

В Java начиная с 8 сделано, что если длина списка в корзине превышает некоторую границу (около 10), вместо него втыкается RBT-based мапа (конкретно в этой корзине).
Таким образом они сокращают эту часть до O(log N). Средний случай всё равно остаётся O(1).

Останнє речення говорить про те, що так писати можна, але це безграмотно:
In practice, however, writing O(n/2) is bad form, since it is exactly the same set of functions as O(n) .

Называть такой скан O(N), сорян, слишком грубо.

Але правильно. Якщо є лінійна залежність від кількості елементів — це O(n). І не важливо чи n/2, чи навіть n/1000000.

всегда существенно меньше N.

Не важливо в скільки чи на скільки менше якщо величина на яку менше константа. Суттєво менше — це O(log n) чи O(sqrt n).

О(1) назвать тоже не могу

Не можеш тому, що час роботи алгоритму не константний і залежить від кількості елементів.

Тут мабуть варто розібратися, що big O нотація означає замість вигадувати якісь O(n/2) чи O(n+1).

Не можеш тому, що час роботи алгоритму не константний і залежить від кількості елементів.

Не зависит он напрямую от количества элементов.
Классика жанра для Trie это О(K) где К — длина ключа, тоесть константа.
Собственно поэтому и хештаблицы частенько пассут задних с их О(1) при вставке и поиске, особенно на больших обьемах данных. С поиском постфиксов ключей не все так просто, но тоже не О(N) и нет прямой зависимости от количества ключей. И не потому что мы остановились сканировать по середине, это какойто частный случай.. Если у нас ключи отсортированы по префиксу, а ищем по постфиксу, то идем до конца дерева. Но этот обход не О(N). Это нужно понимать.

Не зависит он напрямую от количества элементов

Тоді усього два варіанти — або O(1) коли кількість кроків однакова не залежно від розміру колекції, або O(random) — це буде щось нове в CS.

Но этот обход не О(N). Это нужно понимать.

Треба зрозуміти, по-перше, чи залежить час роботи алгоритму від кількості елементів. Якщо ні — це O(1). Якщо залежить — то як само: лінійно — O(n), квадратично чи по логарифму, чи ще який варіант.

Коли ти кажеш «щось середнє між O(1) та O(n)» — це означає складність O(n).

Коли ти кажеш «щось середнє між O(1) та O(n)» — це означає складність O(n).

Это зависит от топологии дерева. Тоесть условно, можно подобрать такую топологию дерева где получим чистый O(n). А можем подобрать другую топологию дерева, где будет О(1).
Топология != Количество ключей. Более того, в многих кейсах мы можем управлять этой топологией и строить именно такое дерево, какое нам удобно (как с контейнером в котором хранятся версии ключей и можно получить срез контейнера (снапшот) на какойто момент времени).

Но в целом я не против. Если ровняем по худшему случаю, то пускай будет

О(N)

Какбы это всеголишь нотация и на конечную скорость работы не влияет.

это всеголишь нотация и на конечную скорость работы не влияет

На швидкість не впливає, але дозволяє оцінити і порівняти різні алгоритми. І чим більше n — тим точніше будуть пропорції.

В том то и дело, что вы настаиваете на нотации, где алгоритм будет равноценен фулскану в тех же хештаблицах или бинарных деревьях. Но это не фулскан. Он вынесет любое бинарное дерево, которое будет пытаться сканировать постфиксы ключей фулсканом за О(N) просто на несколько порядков.

Мені здається ти плутаєш складність та швидкість. Для двох алгоритмів O(n) один може виконуватися 1 мілісекунду, а інший — хоч кілька років. Але те, що вони O(n) говорить нам, що збільшення n пропорційно збільшить час виконання.

І так, чисто з теорії O(1) завжди швидше за O(n), і чим більше n — тим більше різниця. Але це швидкість не у часі, а у кількості операцій на весь набір вхідних даних.

що збільшення n пропорційно збільшить час виконання.

Так в том то и дело. Что на миллион ключей может быть просканировано всего два ключа. И на 100 ключей может быть просканировано 10. Нет пропорциональности. Все зависит от топологии дерева. Вообще давать оценки алгоритмам не такая простая задача как кажется, если это что-то сложнее пузырька или бинарного поиска.

может быть

Не суттєво. Важливо скільки гарантовано — якщо йдеться про пропорційно n (навіть якщо «може бути і менше») то складність буде O(n).

Нет пропорциональности.

Якщо її нема — то це або O(1), або якесь незрозуміле O(random).

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

Ти за цей час міг би почитати про те як складність оцінюють (там не багато) і чим те ж О відрізняється від тета, омега, що таке амортизований час замість вигадувати «між O(1) та O(n)».

Якщо говорити про trie, то його швидкість трохи тупо описувати виключно кількістю елементів (n), бо вставка/пошук геть не залежить від n, тобто дійсно O(1).

Але для trie дуже важливий інший показник — K (довжина ключа в бітах) і B — branching factor (скільки біт на вузол). Тоді вставка/пошук стають O(K/B) а накладні витрати по пам’яті (наче) O(B*n).

edit: хоча фіг його знає як там Бубен свій trie реалізовував, може у нього інша асимптотика. Я писав про класичний спосіб:

struct Node {
  Node* next[1 << B];
  Data* data;
};
для trie дуже важливий інший показник — K (довжина ключа в бітах) і B — branching factor (скільки біт на вузол). Тоді вставка/пошук стають O(K/B) а накладні витрати по пам’яті (наче) O(B*n).

Так, він про це писав вище. І я розумію, що кількість кроків алгоритму пошуку скажімо залежить від довжини ключа, а не від кількості елементів в дереві. Те що я вище написав про колекції взагалі.

Мені здається у випадку trie оцінювати саме найгіршим випадком. А якщо зробити K константою то і до O(1) можна спростити :)

А якщо зробити K константою то і до O(1) можна спростити :)
O(1) still sucks for large values of 1
O(K/B)

Эээ .. и что никого не бомбит ?
Давайте уже признаем, что допускается записывать что-то более точное чем О(1) или О(n) если это имеет смысл в нашем контексте :)

Эээ .. и что никого не бомбит ?
Давайте уже признаем, что допускается записывать что-то более точное чем О(1) или О(n) если это имеет смысл в нашем контексте :)

А почему должно бомбить? :) Контрольный вопрос!

Какова сложность алгоритма для подсчета суммы чисел из двух массивов длиной а и b? :)

int sum(vector<int> &a, vector<int> &b)
  int result = 0;
  for(auto va : a) {
    result += va;
  }
  for(auto vb : b) {
    result += vb;
  }
  return result;
}

O(...) - ? :D

Здесь O(N+M) где N и M количество элементов в векторах а и b.
Здесь констант нет.

Но в примере Юрия

O(K/B)

«B» чистой воды константа. Она может быть например 1, 2 или 8 и всегда постоянна для конкретной имплементации trie.

«B» чистой воды константа. Она может быть например 1, 2 или 8 и всегда постоянна для конкретной имплементации trie.

says who? Без проблем можно реализовать Trie, в котором branching factor переменный, конфигурируется в рантайме (может быть даже разным для узлов разной глубины, если сильно нужно)

Можешь реализовать с переменным.
Только расскажи какая тогда оценка будет ?

O(K/B)

Если B может менятся в рантайме.

O(K/B)

Не катит. Ты даешь общую оценку работы алгоритма, а один и тотже ключ при добавлении может иметь варьированный «B» в рантайме.

Upper bound O(K/B) где B — наименьший B по всем нодам, относящимся к данному ключу.

Ок, тогда я даю оценку своему алгоритму.
О(N*B) где B константа которая всегда больше 0 и меньше 1 и зависит от топологии дерева и природы ключей, также как вверху оценка зависит от бранчинга.

Не можна так.

В тій реалізація, яку показав я, K — параметр структури даних. Тобто грубо кажучи то була не одна структура, а ціле сімейство trie’їв — бери любий. Якщо на C++, то кодом це було б якось так:

template<int B>
struct Node {
  Node* next[1 << B];
  Data* data;
};
тобто немає просто Node, зате є Node<8>.

В ML такі штуки вдало називають гіперпараметрами — тобто перед запуском алгоритму ти фіксуєш деякі «константи» (learning rate, кількість дерев, ...) і тільки потім запускаєш свій алго.

В твоєму ж випадку ти не можеш задати B, тому якось дивно давати оцінку часу в якій фігурує величина, яка тобі геть не підконтрольна («як повезе»).

перед запуском алгоритму ти фіксуєш деякі «константи»
branching factor переменный, конфигурируется в рантайме

Ключове слово «конфигурируется». В твоїй «оцінці»

О(N*B) где B константа которая всегда больше 0 и меньше 1 и зависит от топологии дерева и природы ключей

B просто з’являється, а не опція.

Там ключевое слово рантайм. Тоесть параметр меняется после запуска.

O(K/B)
Эээ .. и что никого не бомбит ?

Разумеется, не бомбит. В определении, которое дал Юрий, B это не константа, а переменная, от которой зависит сложность.

Давайте уже признаем, что допускается записывать что-то более точное чем О(1) или О(n) если это имеет смысл в нашем контексте :)

Дык, запиши что-то более точное. «Меньше чем O(N), зуб даю» это не немного точное, а демонстрация того, что ты сам не понимаешь сколько именно там или понимаешь настолько плохо, что не можешь сформулировать.

ты сам не понимаешь сколько именно там или понимаешь настолько плохо, что не можешь сформулировать.

Я уже сформулировал. Зависит от топологии дерева. Читай, природы ключей. К сожалению мир не чернобелый.

Я уже сформулировал. Зависит от топологии дерева. Читай, природы ключей. К сожалению мир не чернобелый.

Т.е. где-то около O(IT_DEPENDS)

Выращивать решения можешь? Нет? Тогда я впереди тебя.

Я пошутил. У меня мало что есть пока. Только идея (возможно у тебя подсмотренная) и немного кода. Возьмем например алгоритм факторизации. Как одновременно получить несколько тысяч (или больше) непроверенных решений?
Помнишь картинку твою, где дерево растет?
А дальше и того проще — обнаружить ID ветки верного решения в общем русле вывода.

Не хочешь заменить Артема и переписать харрей на раст ? :)

Нет времени. Полно своих недоделок и недоработок и все они требуют внимания и заботы прямо сейчас и даже одновременно. К счастью, они все время сближаются друг с другом и это обнадеживает тем, что когда-нибудь все закончится финальным релизом.

Попил кровушки С++ со своими темплейтами, но вроде получилось.
Запилил такой дженерик синтаксис.

HArrayGeneric<std::string, std::string> ha;

ha["dou"] = "hello";

std::string sayHello = ha["dou"];

Работает с любыми типами обьектов, по идее. Вызывает конструктор копирования. Сделал пул обьектов, правда шринки страниц не делал, но они просто делаются поскольку обьекты одного размера на страницах.

Есть сомнения на счет этой строчки, скорей всего это неправильно, но пока что это работает
github.com/...​/src/HArrayGeneric.h#L239

Проблему с непрерывной областью памяти решил следующим образом.
Если это какойто стандартный тип, строка, вектор и тд. То есть стандартные реализации getKeySegments, ничего делать не надо. Если это какойто сложный кастом класс, то нужно сделать свою реализацию getKeySegments. В простейшем случае просто вернуть ссылку на начало своей области памяти и ее размер без байт выравнивания (чтобы в конец ключа не попал муссор).

Сделать что-то более сложное (итераторы и тд) пока не представляется возможным, ввиду недостаточных знаний по глубине глубин темплейтов в С++.

В целом моя рекомендация следующая. Если не фанат темплейтов, то лучше использовать стандартные методы в стиле Си. Их не много, они с простым интерфейсом и работают максимально быстро.

insert
getValueByKey
delValueByKey
scanKeysAndValues
Если фанат темплейтов, то можно использовать HArrayGeneric, а лучше по ходу дела его допилить.

У меня вот такая сортировка получилась.
Видео сортировки
На java. А вот ссылка на гитхаб
Кто-нибудь может посмотреть, насколько это вообще полезный софт?
Может я велосипед повторил?
И еще вопрос: как harray с java? Дружит ли?

Тебе нужно сначала понять, твой алгоритм использует доп память или нет.
Если не использует, то тут поле сильно сужается. Классика это квиксорт. И лучшее что пока что придумали это тимсорт. Разница между квиксортом и тимсортом, что тимсорт лучше работает с данными которые уже частично отсортированы. Что в реальной жизни встречается часто. Тимсорт тотже квиксорт, но с вкраплениями шелла на нижних уровнях.

Если алгоритм использует доп память. То вариантов тут зоопарк в кубе. Эти алгоритмы могут работать быстрее чем квиксорт или тимсорт. Даже харрей может работать быстрее чем квиксорт, по крайней мере на некоторых примерах, но он использует доп память, а значит в битвах сортировок не участвует.

Я обмениваю две случайные ячейки, если условия ок ( содержимое соседних ячеек допускает обмен) при этом не уменьшаю размер растущих одноцветных областей.
Правда, код пока сырой, оптимизаций еще вагон можно добавить.
Только вот надо ли оно? Где это применяется? Мне ведь приснилось просто, я и написал. По наитию. Теперь бы найти применение.

На java. А вот ссылка на гитхаб

И как эту свалку рандомных файлов запускать? Сомневаюсь, что кто-то пойдёт в этом копаться.

Выделите нормально код самой сортировки применимо, например, в варианте ${package}.sort(List theArray, Comparator comparator).

Сделайте проект под управлением Maven. Сложите в него пакет с библиотекой и какую-нибудь простую запускалку в духе прочитать int’ы из stdin и выдать результат в stdout.

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

Вообще-то Валентин совсем не много попросил.
Вот представьте, были бы вы какимто физиком-теоретиком. Пойди докажи что какойто мюон планковских размеров в квантовой механике, с временем жизни десять в минус двадцатой вообще существует. Пришел ты такой на форум и никаких шансов доказать его сущестования нет, кроме как закопать охулиард денег на коллайдер. А в коде и один в поле воин, если написал то получил пруф. И все вопросы сняты. Ну или почти все.

Вот это вы верно заметили. Все эти мюоны они для грантов выдумали.
Но токмо Валентин попросил разжевать про хлебушек. Там жеж все криминально просто.
Развернуть зип в эклипс-воркспейс да и того. Run как бы, без параметров. Ну нету мейна у javafx, так ведь не запилили изобретатели.

Развернуть зип в эклипс-воркспейс да и того.

Хм, это уже на что-то похоже. Ладно, попробую по свободе...

На java. А вот ссылка на гитхаб

Сорри но по ссылке куча не читаемого мусора с закоментированным кодом и .java с .class вперемешку....

Кстате к теме Integer. Это интересный вопрос.
В свое время, разработчики еще первых компиляторов, ввели int как стандарт целого числа. После этот стандарт распространился на все языки. Почему они сделали целое число знаковым по дефолту, понятно. Чтобы уменьшить количество ошибок в коде с переполнением. Такая себе защита от дурака. Сделали это ценой того, что потеряли по сути 50% полезных значений в каждой переменной.
Нормальному низкоуровневому хорошо отлаженому коду, не нужен этот ванильный signed int. Он использует везде где это можно только uint32, unsigned int. Ровно так, как целое число хранится в памяти, все 32бита в твоем распоряжении.

Нормальному низкоуровневому хорошо отлаженому коду, не нужен этот ванильный signed int. Он использует везде где это можно только uint32, unsigned int. Ровно так, как целое число хранится в памяти, все 32бита в твоем распоряжении.

Я не согласен, но повторять всю недавнюю дискуссию не хочу.

Пипиши алгоритмы на Rust (где все индексы в массивах и мапах — беззнаковый usize ). Засекай через сколько дней ты завопишь и начнешь требовать старый добрый signed int для индексации.

Засекай через сколько дней ты завопишь

Лолшто. Юзаю только size_t

Похоже, что в С++ тоже беззнаковый тип для индексации, что для меня еще одна причина не юзать С++.

У тебя могут быть массивы отрицательной длины? А комплексная длина для массивов уже тоже возможна, но только после пятой затяжки?

Ви хотіли пошуткувать, але у вас не вийшло

Адресную арифметику никто не отменял.

Который вообще равно определён как signed int

Обычно signed long как минимум (на шибанутой Windows — signed long long), потому что по битности должен быть равен указателю, и давать минимум половину размаха его значений.

У меня почему-то абсолютно все построено на офсетах, смещениях, прыжках по памяти. И почему-то ниразу сходить ниже нуля не понадобилось. Более того, с оверфлоу оно даже как-то поспокойней, сразу крешится. А если сходит тихо на −1 офсет и пойдет дальше, иди потом ищи ошибку.

А у меня нет. Например указатель на данные а мета перед указателем. Например буфер аллоцированный на куче.

Более того, с оверфлоу оно даже как-то поспокойней, сразу крешится

А если я захочу твою штуку использовать в ОСРВ без защиты страниц памяти или вообще на микроконтроллере? :-)

Ирония в том что весь контейнер чуть менее чем полностью состоит из прыжков по памяти по офсетам. Это означает что контейнер на уровне своей песочницы памяти уже работает без защиты страниц памяти. И мне пришлось реализовать свой продвинутый механизм проверки страниц памяти в самом контейнере, который чекает консистентность страниц, дабы отлавливать баги как можно раньше в рантайме.
github.com/...​aster/src/HArray_test.cpp
Получается двойная линия обороны. Базовая от оси, которая чекает ненормальное поведение процесса и селф чек самого контейнера. С таким уровнем двойной защиты, даже если планка памяти случайно сбойнет, харрей имеет средства это обнаружить во время рантайма, во время бенчмарков. Не смотря на весь пц не читаемости кода, это действительно один из самых отлаженных кодов в котором я полностью уверен. И если у меня ошибка в приложении, баг в самом харрее я ищу в последнюю очередь. Потому что баг там сложно найти, даже если там будет миллиард ключей и десять миллиардов джампов по памяти. Все что может прилететь, std::bad_alloc. Память закончилась, но тут уже ничего не поделаешь.

signed int

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

Таких примеров кстате не мало. Вот говнокодеры 60х признались что нулл можно было и не вводить.
www.infoq.com/...​ollar-Mistake-Tony-Hoare

Мне надо было шифтить биты в целых числах, там начались чудеса.

Не знаю что за чудеса, но в любом случае, переходи на Rust на теплые ламповые i32 и u32, где нет никаких непонятнок по поводу того как будет работать тот или иной оператор, все задокументировано без неопределенного поведения.

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

Отрицательные числа тоже целые

на Rust

Си или Раст или Питон, это все безделушки. Хомячки одни и те же идеи переписывают слева направо и справа налево.
Будущее за аспектным программированием. Это самая значимая идея с времён ООП ксерокса и смоллтолка. Просто это пока не очевидно.

Майбутнє за відсутністю програмування як класу. А перед цим ще буде етап розробки систем, які будуть імітувати роботу мозку. Та відповідні мови програмування з’являться для цього.

які будуть імітувати роботу мозку.

"Зачем искусственно выращивать людей, если каждая баба может нарожать их без всякой медицины"© проф. Преображенский

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

Імітація мозку треба для того, щоб потім вчити програми, а не писати їх.

Я вже прийшов до того, що в моєму коді немає явних IF та FOR в так званій «бізнес-лозіці». Та активно використовуються підходи fail safe та fault tolerant. Та все працює роками без змін та помилок, які видні кінцевому споживачеві.

Але аспектне програмування вже вмерло при народженні. За мервячиною немає майбутнього.

непонятнок по поводу того как будет работать тот или иной оператор

Ноги у цього ростуть ще з С де усе було близько до заліза і проци мали різні інструкції для знакових та беззнакових типів.

Нет, всё хитрее. Разные инструкции ни при чём.
В раннем C операции выполнялись буквально и с усечением значений, но так как не у всех процов были отрицательные числа в дополнительном коде, в стандарте была оговорка «а если вы тут получите переполнение, мы не отвечаем за результат». Но все знали, что это только оговорка.
А потом это «джентльменское соглашение» было нарушено, когда авторы компиляторов заметили, что можно начать использовать приёмчики вида: если x — signed, то проверки типа x+1>x автоматически менять на true. Получив на этом выигрыш, не могли уже дальше остановиться.
Для беззнаковых стандарт гарантировал усечение значения, поэтому так хакнуть его не получилось.
(И вот за что я ругаю Столлмана при остальных его безусловных достижениях — что он был первым среди таких абьюзеров стандарта.)

переходи на Rust на теплые ламповые i32 и u32, где нет никаких непонятнок по поводу того как будет работать тот или иной оператор, все задокументировано без неопределенного поведения.

Ну да, просто в debug одно, в release другое.
(Да, я знаю про методы типа checked_add)

Ну да, просто в debug одно, в release другое.

Это то, что мне в Rust не нравится.
Но в защиту Rust — это просто дефолтное поведение, которое конфигурируется на уровне проекта. Если нужно, чтоб в debug и release было одинаковое поведение, это можно указать:

doc.rust-lang.org/...​iles.html#overflow-checks

(Да, я знаю про методы типа checked_add)

Это очень годные методы, и встроены в стандартную библиотеку, что збс. Я знаю, что в С есть разные __builtin_mul_overflow, но камон, куда их засунули это просто ппц. Не сравнить с тем где они лежат в Rust.

Там еще есть unchecked_add для любителей триггернуть неопределенное поведение.

Я знаю, что в С есть разные __builtin_mul_overflow, но камон, куда их засунули это просто ппц.

Это, увы, ещё не стандарт, это GCC расширение, принятое и в Clang.

Там еще есть unchecked_add для любителей триггернуть неопределенное поведение.

Ну я считаю, что такие вещи должны задаваться контекстом, чтобы не писать такое на каждую операцию (но и явные функции тоже должны быть).

Если нужно, чтоб в debug и release было одинаковое поведение, это можно указать:

Это хорошо.

Таких примеров кстате не мало. Вот говнокодеры 60х признались что нулл можно было и не вводить.

Еще одна причина переходить на Rust: в Rust нет null

Еще одна причина переходить на Rust: в Rust нет null

Option (его None) это такой же null, только чуть более явный.

Option (его None) это такой же null, только чуть более явный.

Он мягко говоря просто ппц насколько более явный.

Одно дело, когда получаешь Person в Java и не понятно, там что-то лежит или там может быть Null, надо делать проверку или нет?

Другое — когда получаешь Person в Rust, который гарантированно не null, который можно сразу читать. Или Option, что дает вполне определенный сигнал — тут может быть None, и у тебя нет возможности прочитать значение, не сделав проверку и не обработав случай с None.

Одно дело, когда получаешь Person в Java и не понятно, там что-то лежит или там может быть Null, надо делать проверку или нет?

Там уже наделано средств, которые, например, делают проверки — если генератор этой Person не помечен как notnull, то надо проверять, а если помечен, то проверяет честность этой пометки.

Другое — когда получаешь Person в Rust, который гарантированно не null, который можно сразу читать.

Ну да, эти проверки ввёрнуты в обязательную функциональность компилятора.

Person в Java и не понятно, там что-то лежит или там может быть Null, надо делать проверку или нет?

Эмммм, уже давно можно получать не Person, but Optional.

Проблема переполнения ушла в джаве, шарпе, жаваскрипт, а обрезанный инт остался

Проблема переполнения ушла только в Python, Erlang и т.д., где int безразмерный (но и операции с ним стоят как 10-50 обычных). В названных она в полный рост, только решается везде по-разному.

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

signed int это целое.

Вот говнокодеры 60х признались что нулл можно было и не вводить.

Не было там ошибки, что бы Хоар сейчас ни говорил.
Было отличное решение для того времени, которое помогло справиться со сложностью на десятки лет вперёд.

Засекай через сколько дней ты завопишь и начнешь требовать старый добрый signed int для индексации.

Я для своих проектов делал небольшие врапперы-адаптеры над STL только ради того что бы юзать обычный int для индексов и размеров )

Я там тобі зробив pull request в якому додав cmake — тепер можна генерити ним мейкфайли і проекти для вінди, лінукса та мака.

Спробував додати CI, але чомусь release білд на github крешиться на всіх трьох ОС — github.com/...​ay/actions/runs/988767880.

Глянул комит — много написал

Как ты успеваешь активничать в strava, работать фултайм в FB, заниматься семьей и еще и лячкать в опенсорц? О_о

Там 90% коду згенеровано однорядковими скриптами.

А так — зміна активності лише допомагає. Поки працюю думаю про тренування. Поки тренуюся — думаю про книги, музику, фільми. Поки їм — читаю чи дивлюся щось. Спати страшенно не люблю.

Как ты успеваешь

завязали с Швецией :)

Спасибо за пулл реквест.
По поводу краша.
Везде в коде изменено uint32 на int32_t
С этим есть 2 проблемы.
1. int32_t это integer со знаком, по простому int. uint32 это unsigned int.
Это меняет логику в алгоритме и он крашится.
2. int32_t как и uint32_t определен как
typedef int int32_t;
а это значит что в 32х битном процессе он будет 4 байта, а в 64х битном 8 байт. Это сенситив для логики работы. Всегда должно быть ровно 4 байта (32 бита). Кстате можно переписать и использовать все возможности х64 систем в будущем и получить буст по производительности, сравнивать за раз не 4 байта а 8, но это трудоемкая задача.

Все твои изменения замержил в мастер, но откатил логику с int32_t и убрал креш.
Гуд джоб!

PS: Взялся за дженерик реализацию с темплейтами HArrayGeneric, чтобы писать код в стиле std::map. Думаю до конца недели выкачу первую версию.

typedef int int32_t;
а это значит что в 32х битном процессе он будет 4 байта, а в 64х битном 8 байт.

С чего такое утверждение?
И во всех Unix, и во всех Windows таргетах int — 32 бита, даже если таргет 64-битный.
Вы, наверно, путаете с long. Вот там на 64-битных таргетах 64 бита в Unix, 64 в .NET, но 32 у б-гомерзких извращенцев в Windows C/C++.

Это меняет логику в алгоритме и он крашится.

А что именно меняется?

А что именно меняется?

Там есть шифты битов, подозреваю что они будут по разному работать для int и unsigned int.
Подробнее копать краш смысла нет, поскольку это идеологический вопрос.
dou.ua/...​rums/topic/18849/#2169427

Там есть шифты битов, подозреваю что они будут по разному работать для int и unsigned int.

Ну в общем да. Причём сдвиг знакового влево — UdB, если его считать целочисленным переполнением, а сдвиг вправо — F-деление со знаком.
Увы, это C.

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

Не согласен. Но обсудим там.

Я бачу ти додав sln назад — він не потрібен, його cmake генерить. filters — це взагалі спецефічний для юзера файл.

Мне просто так удобней, сорри.
Я через Винду работаю. У нас на всех проектах есть sln, через make нигде не собирают, хотя наверно есть такая возможность. filters я добавлю в гитигнор.

На вінді так і правда зручніше. Але cmake дозволяє робити проекти та мейки практично для будь-чого просто однією командою. І тоді проекти студії будь-якої версії можна згенерити за секунди коли вони будуть потрібні.

В цілому моє діло запропонувати, ти звісно роби як тобі здається правильно, тут у мене ніяких претензій.

Мне просто так удобней, сорри.

Попробуй cmake освоить
Забудешь про ручные проекты студии в большинстве случаев

Если у тебя есть кастомные настройки проекта, например debug home folder или что то еще, это можно сохранить в файле .filters (вроде так) и хранить его в своем гите для настройки

Обернешь генерацию проекта в скрипт, который и проект под студию сгенерирует, и папки дебага с конфигами создаст. Старт проекта должен быть в 1 клик мышкой! =)

Здесь несколько человек возмущалось, что у автора читерские бенчмарки, где он сравнивает с коллекциями, у которых совершенно другие цели и функциональность.

Так вот, предлагаю автору to pick on somebody your own size и пробенчмаркать с чем-то похожим. Например: github.com/Tessil/hat-trie (первое что выдал гугл на fast trie)

Заодно и посмотришь, как эту хрень завернули в нормальный юзер френдли контейнер

Это симбиоз хештаблицы и трай.
С моей реализацией сравнивать некорректно. У меня намного больше функциональности, типа сортированные данные, сжатие, сканы по диапазонам, и шаблонам ключей, удаление с демонтажем ключа и шринками страниц и тд.
По поводу темплейтов, скорей всего пошли по пути хештаблиц, там ключ хранится целиком в непрерывной памяти. Но нужно читать код.

лол, а с стдмапой корректно?

Да, потому что она тоже содержит сортированных данные и предоставляет интерфейсы поиска по диапазону.
В моей СУБД харрей может использоваться как движок для индекса, как и бинарные деревья. Хештаблицы — нет. Они для аналитических запросов не катят.

в обычных субд я могу построить индекс на числах с плавающей точкой. В мапу тоже могу их присунуть. А вот в harray чет не могу.

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

в обычных субд я могу построить индекс на числах с плавающей точкой. В мапу тоже могу их присунуть. А вот в harray чет не могу

Можешь. Это ещё одно отличие взрослой имплементации от академической, которая работает только со строками.

//Set float comparator for right sorting
//Another options: setStrComparator, setInt32Comparator, setUInt32Comparator
//or define your custom comparator through setCustomComparator
ha.setFloatComparator();

Глянул код по диагонали. Они темплейты вроде как реализовали только для строк и примитивных типов. Моя мапа работает с любыми бинарными данными, не только с строками. Поэтому хотелось бы более универсальное решение с шаблонами

А кто стандарты С++ хорошо знает ?
Хочу написать такого плана метод.
Какой абстрактный тип здесь должен быть вместо Т ?

void CopyFunc(T obj)
	{
		char arr[1000];

		std::copy(obj.begin(), obj.end(), arr);
	}

Это ж С++.

Делай шаблюнную функцию и просто фигач T без указания типа.

Ок, а как сделать развилку ?
Если обьект имеет методы begin, end то в if, если не имеет то в else ?

Смысл если Т поддерживает итератор, читать данные в буфер через итератор, иначе через адресс обьекта в памяти

void CopyToBuff(T obj, char* buff)
{
       if(HAS_ITERATOR(T)
	{
                  std::copy(obj.begin(), obj.end(), buff);
        }
       else
        {
                  for(int i=0; i<sizeof(T); i++)
                          buff[i] = (char*)&obj[i];
        }   
}

Тебе нужно 2 функции. Сложное, но надежное решение — это вывернуться через SFINAE с std::enable_if, определив что переданный контейнер Т может быть проитерирован через begin/end. Готового енейблера нету, прийдется написать велосипед. На stackoverflow народ уже решал эту проблему, поищи. Разные решения предлагают.

Простое и ненадежное решение перегрузить 2 функции:


// Example program
#include <iostream>
#include <string>
#include <vector>

template<typename T>
void CopyToBuff(T obj, char* buff)
{
    std::cout << "generic pointer\n";
}

template<typename C>
void CopyToBuff_STL(const C& obj, char* buff)
{
    std::cout << "STL generic\n";
}

template<typename T>
void CopyToBuff(const std::vector<T>& obj, char* buff)
{
    CopyToBuff_STL(obj, buff);
}

int main()
{
    struct Foo {};
    Foo object;
    char buffer[42];
    
    CopyToBuff(object, buffer);
    
    std::vector<int> anotherObject;
    CopyToBuff(anotherObject, buffer);
}
Ненадежное потому что если попытаться скормить list, а перегрузки нет — то вызов пойдет в версию которая generic.

Можешь подсказать, в чем тут может быть дело ?
Есть простейший класс. Почему всегда и на set и на get вызывается одна и таже ф-ция ?

template <typename K, typename V>
class Array
{
public:
	const V& operator[](std::size_t idx) const
	{
		V v;
		return v;
	}

	V& operator[](std::size_t idx)
	{
		V v;
		return v;
	}
};

int main()
{
	Array<int, int> arr;
	arr[0] = 1;     //V& operator[](std::size_t idx)
	int x = arr[0]; //V& operator[](std::size_t idx). Why not const V& operator[](std::size_t idx) const ?
}

Если я правильно помню, в C++ у оператора [] нет понятия get/set. Это функция, которая возвращает ссылку на какое-то значение. Если ты пытаешься сделать get, ты получаешь эту ссылку и что-то с нее читаешь. Если ты пытаешься сделать set, то ты записываешь новое значение по этой ссылке.

Разница между const/не const состоит в том, что по конвецнии значение, которое объявлено как const, нельзя изменять. То, которое не объявлено как const изменять можно.

В твоем случае Array<int, int> arr; не объявлен как const, поэтому в обоих случаях вызывается non-const реализация оператора.

Тяжело понять логику С++ разработчиков.
Получается нужно писать гибридный метод insertOrGet. Который будет инсертить и возвращать ссылку на велью если такой ключ не существует. И если ключ существует, то будет возвращать ссылку на уже существующее велью.
Это замедлит работу. Потому что insert != get. Я думаю замедление будет в районе 10-15% только из-за слоупоков которые не смогли продумать базовый нормальный сценарий перегрузки операторов в С++ (кстате в Шарп с этим все уже нормально)

Ну не уверен что 10-15%. Константность все равно можно снять. Либо не снимать, а втупую прочитать память. Насколько мне известно это не тот модификатор (const) который позволяет компилятору оптимизировать код.
Поэкспериментируй с godbolt.org

#include <cstdlib>

struct vector3f {
    vector3f() {
        v[0] = rand();
        v[1] = rand();
        v[2] = rand();
    }

    float v[3];

    float& operator[](size_t i) {
        return v[i];
    }

    const float& operator[](size_t i) const {
        return v[i];
    }
};

int main(int argc, char ** argv)
{
    vector3f v1;
    const vector3f v2{};

    int a = v1[argc];
    int b = v2[argc];

    return a + b;
}
Вот такой примитивный код или идентичный ассемблер выдает или с разницей в 1 инструкцию

Еще можно экспериментировать с тем же самым, только main поменять на

int main(int argc, char ** argv)
{
    vector3f v = vector3f{};
    int a = v[argc];
    return a;
}
и вставлять/убирать const. Компилятор иногда оптимизирует изза наличия двух переменных.

Это хорошо работает с вектором, массивом и тд. Когда операция Get == Insert/Update. А теперь представим что под капотом что-то сложнее, например обычный однонаправленный связной список. Получается операция insert, может быть за О(1), если мы будем хранить хвост списка. А операция поиска О(N) поскольку нужно бежать по всему списку с самого его начала.

Это глупое решение разработчиков С++. Которые простейшую операцию сделали неочевидным способом и заточили ее под случай
Get == Insert/Update без всякой веской причины, что неверно в многих других случаях.

Ну тогда разноси на несколько методов get/insert/update
Будет single responsibility в классическом его виде )

Сейчас так и есть :) Но хотят же чистый ванильный синтаксис с темплейтом [ ]
Чтобы писать в таком няшном стиле.

HArray <int, int> arr;
arr[0] = 1;
Тяжело понять логику С++ разработчиков.
Получается нужно писать гибридный метод insertOrGet. Который будет инсертить и возвращать ссылку на велью если такой ключ не существует. И если ключ существует, то будет возвращать ссылку на уже существующее велью.

Да. Потому что [] это наследие массивов.
Для более прямо определённых операций типа «проверить есть ли такое», «вставить с защитой от дублирования» и т.п. — есть свои отдельные названия.

Посмотрите на интерфейс стандартного std::map.

Там нижче правильно сказали про масиви. Подивись на std::map::at() як альтернативу.

Тяжело понять логику С++ разработчиков.

я вот купил другую книгу, чтоб понять, буду читать:
www.amazon.com/...​_asin_title?ie=UTF8&psc=1

Получается нужно писать гибридный метод insertOrGet. Который будет инсертить и возвращать ссылку на велью если такой ключ не существует. И если ключ существует, то будет возвращать ссылку на уже существующее велью.
Это замедлит работу. Потому что insert != get. Я думаю замедление будет в районе 10-15% только из-за слоупоков которые не смогли продумать базовый нормальный сценарий перегрузки операторов в С++ (кстате в Шарп с этим все уже нормально)

Можно посмотреть, например, тот же std::map и найти, что там есть методы для выполнения всех кейсов, а именно:

at
[]
insert
insert_or_assign
emplace
erase
find

тысячи их

arr не константная переменная
Подробно описано в „16.3 Overload resolution” www.open-std.org/...​ocs/papers/2017/n4713.pdf , в частности 16.3.1.2

The set of candidate functions can contain both member and non-member functions to be resolved against
the same argument list. So that argument and parameter lists are comparable within this heterogeneous
set, a member function is considered to have an extra parameter, called the implicit object parameter, which
represents the object for which the member function has been called. For the purposes of overload resolution,
both static and non-static member functions have an implicit object parameter, but constructors do not.

Насколько я понял написанное , arr[0] эквивалентно вызову
operator[](arr, 0).
А таких operator[] будет 2:
operator[](const Arr&, size_t)
operator[](Arr&, size_t)
Вот он и выбирает по best candidate.

Бубен, ты полез в дебри, в которые даже я бы лезть не стал.

Это хорошая идея — каждый объект это просто блок памяти, который можно скопировать. В реальности в С++ это все совсем не так, как ты предполагаешь и интерпретировать объект просто как блок памяти нельзя.

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

Привести какой-то умный объект типа того же вектора или unique_ptr к массиву байт можно, но как только ты начнешь эти данные куда-то копировать, ты начнешь сразу нарушать инварианты, на которых этот объект построен и это в итоге приведет к неопределенному поведению, крашам, утечкам памяти и прочей фигне.

Например, если тебе передадут shared_ptr (умный указатель с счетчиком ссылок), и ты его сокопируешь побайтно, ты создаешь новую ссылку на внутренние данные, не увеличив счетчик ссылок. В определенный момент счетчик ссылок дойдет до 0 и память будет освобождена, но в твоей мапе так и будет лежать неучтенная ссылка на эту памяти и кто-то может ее получить и обратиться к этой памяти (aka dereference dangling pointer), что в С++ является неопределенный поведением.

У каждого из этих умных типов данных свое поведение, которое нужно строго выдерживать. Хардкодить разные случаи как ты пытаешься тут сделать нет смысла, так как любой разработчик может добавить свой тип со своим жизненым циклом и твой контейнер не будет с ним нормально работать, если ты его не реализовал в максимально обобщенном виде с учетом конвенций С++.

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

Как я понимаю, тебе нужен участок последовательной памяти размером с объект, который ты пытаешься скопировать, тебе нужен указатель в эту память и он должен быть правильно выровнен для этого типа. После этого ты вызываешь copy или move конструктор.

Также тебе нужно будет запилить copy/move конструкторы для твоей мапы, и деструктор, который будет вызывать деструкторы для данных, которые ты в своей мапе хранишь (про это тоже не нужно забывать).

После этого ты вызываешь copy или move конструктор.

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

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

Суть такова. В С++ нельзя перенести объект из одного места в памяти в другое место в памяти, скопировав его по байтам. Если ты это делаешь, ты нарушаешь инварианты, которые делал разработчик этого объекта, что впоследствие ведет к утечкам памяти, не определенному поведению и уязвимостям.

Представь себе, ты сидишь на стуле. Тебя кто-то хочет посадить на другой стул. Вместо того, чтоб попросить тебя «Бубен, сядь пожалуйста на вот тот стул», берут пилу, распиливают тебя на куски, сливают кровь в ведро, отностят куски к новому стулу, склеивают изолентой, заливюат кровь обратно. Физически это тот же Бубен, но он уже немножко не такой как был раньше. Когда ты закричишь «Что вы делаете! Прекратите меня резать», тебе скажут — «Слушай почему кричишь? Мы вот вчера фигурку из пластелина так же разрезали, перенесли на новое место и склеили — посмотри, сидит как новенькая».

Есть куча причин почему это делать нельзя.
Примеры:
self-referential classes (одно из полей класса ссылается на другое поле. При перемещении этого объекта в памяти нужно поля обновить, чтоб они ссылались в новое место в памяти)
Смарт поинтеры (при перемещении нужно или обновлять счетчики или инвалидировать старые указатели)
Объекты, у которых копирование/перемещение запрещено. Двигать такие объекты в памяти или копировать куда-то вообще нельзя.

Повторюсь, никто не запретит тебе получить сырой указатель на объект и скопировать побайтно, но с таким же успехом ты можешь, например, взять и отформатировать диск на компьютере пользователя при добавлении нового ключа — валидность этих двух действий будет идентичной.

Правильный каноничный способ это делать — как тебе тут я и Олександр пытаемся объяснить. Можешь еще залезть в потроха того же std::unordered_map или std::vector чтоб понять как там сделано и попытаться воспроизвести.

Ты натягиваешь свое знание С или очень древнего С++ на современный C++, в котором все по-другому.

Если хочешь осознать уровень п****ца и понять почему от С++ надо держаться подальше и оставаться на старом добром С (или переходить на Rust), почитай:

www.amazon.com/...​_asin_title?ie=UTF8&psc=1
www.amazon.com/...​_asin_title?ie=UTF8&psc=1

Дауж. Такая простыня комента с выжимкой

залезть в потроха того же std::unordered_map

И самому разбираться.
Спасибо.

Я тебе дал ссылки на книжки, в которых все это описано в подробностях с примерами. Это не тема, которую можно сжать в один пост, или где можно дать готовый код, который ты себе скопипастишь.

Есть причины по которым С++ имеет репутацию ипанутого языка, который нельзя выучить за 21 день, смекаешь?

репутацию ипанутого языка

С этим я согласен. Поэтому и нужна помощь написать такой врапер. Со своей стороны могу подогнать любой интерфейс любой пул для хранения ключей и велью.
Но экспертизы по С++ мне не хватает чтобы морочится с темплейтами. И ещё хуже что в моих задачах и так все классно работает без темплейтов. Поэтому задача найс ту хев.

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

Все, что ты можешь хранить в общем случае — это так называемый «trivial type» — скалярный тип, не имеющий конструкторв, деструкторов, без наследования и т.п. Поищи is_trivial тут: en.cppreference.com/w/cpp/types/enable_if

По иронии даже std::string не является trivial, тебе нужно будет написать несколько специализаций на нетривиальные типы (которые все равно могут быть безопасно представлены как набор байт).

Этот врапер написать невозможно

Завтра подумаю можно ли обойти эту проблему через какой-то трюк.

Подумай еще об этом:

В сложных структурах с несколькими полями будет место, которое зарезервировано для выравнивания полей (aka padding). В этом padding лежит мусор.

Если ты напрямую переводишь значение в последовательность байт с помощью memmove, ты скопируешь и этот мусор и вставить его в свою мапу. Это создаст проблему — ключи, которые логически идентичны (если сравнивать значения полей) могут иметь разное представление в памяти за счет мусора в padding. Поскольку твой код не отличает padding от самих полей, возможна ситуация, когда мы вставили ключ в твою мапу, а потом пробуем что-то извлечь по тому же ключу (но уже с другим мусором в padding), но она ничего не найдет.

Подробнее: wiki.sei.cmu.edu/...​ not compare padding data

Надеюсь, ты сможешь заснуть с этим заннием.

ты чет явно не то делаешь. Трай — это структурка для строк, она может работать только со строками. В отличие от мапы, которая работает с любым типом, на котором определен порядок. И в отличие от хештаблицы, в которую можно засунуть все, что хешируется

вот value теоретически может быть любым обьектом, да

Трай — это структурка для строк

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

Нет никаких мат препятствий хранить любой список байт

обьект — это не список байт, тебе артем это уже 20 раз написал. Ты можешь юзать только список байт как ключ. Мапа может юзать что угодно как ключ, хоть другие мапы. Не пытайся натянуть функционал мапы на себя

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

зачем такое делать, ведь поиск по префиксу и итерация потеряют смысл, и ты скатишься до обычного key-> value? а так то да, можно хрвнить в пуле, брать от каждого обьекта md5 и пихать его как ключ

чем может быть мне полезен копи конструктор

Тим, що в С++ це єдиний спосіб правильно скопіювати об’єкт.

Если обьект имеет методы begin, end то в if, если не имеет то в else ?

Дві різних реалізації. Напиши чи візьми з якоїсь ліби is_iterator, щось типу stackoverflow.com/...​trary-type-is-an-iterator.

Не певен, що правильно зрозумів твоє питання. Щось таке:

template <class T>
void CopyFunc(const T& obj) {
  vecor<T> arr;

  std::copy(obj.begin(), obj.end(), arr.begin());

А вот и Монады подвезли.
Классический пример монадической функции с шарпа.

var dic = new Dictionary<string, List<int>>();
//fill dictionary
IEnumerable<int> joinAllListsIntoOne = dic.SelectMany(x => x.Value);

В харрей это все работает бай дизайн, без каких либо Linq костылей.
И работает за O(N) где N количество элементов в списках, по которым бежит итератор
scanKeysAndValues

Если продолжить идею монад, например заджойнить не все списки, а только ключи которые начинаются на StartKeyFromPattern

var dic = new Dictionary<string, List<int>>();
//fill dictionary
IEnumerable<int> joinAllListsIntoOne = dic.Where(x => x.Key.StartsWith("StartKeyFromPattern")).SelectMany(x => x.Value);

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

Ти дуже хитро все це звів до unit32 :) А з std::string так зможеш? Чи просто з якимось невідомим типом T?

PS. Промазав з коментом — це відповідь до написаного тобою нижче.

Вот врапер который полностью универсальный. Принимает в качестве ключа любой массив байт и в качестве велью любой массив байт.
github.com/...​aster/HArray/HArrayChar.h

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

HArrayChar ha;

	ha.insert("a", 1, "b", 1);

	char value[16];
	uint32 valueLen = 0;

	ha.getValueByKey("a", 1, value, valueLen);

Все тоже самое, но с std::string

HArrayChar ha;

	std::string key = "a";
	std::string val = "b";

	ha.insert(key.c_str(), key.length(), val.c_str(), val.length());

	char value[16];
	uint32 valueLen = 0;

	ha.getValueByKey(key.c_str(), key.length(), value, valueLen);

	value[valueLen] = 0;

	std::string outVal(value);

Пример немного кривоватый, делает лишнее преобразование в конце. Но при желании сможешь написать свой врапер HArrayString.h на основе HArrayChar который будет принимать строго интерфейсы std::string

Так, але знову ж таки тут зручно те, що string можна звести до char[], а той в свою чергу є просто буфером. Але таке не буде працювати з довільними типами ключа та значення і доведеться писати врапери для кожної комбінації типів і в них робити ці перетворення між типом та масивом байт. А це стає особливо складно коли треба буде глибоке копіювання робити.

В той же час map працює для будь-якого типу для якого визначені конструктор по замовчанню та конструктор копіювання хоча б.

Тобто твій враппер мав би виглядати приблизно так:

template <class Key, class Value>
class HArray {...};

До речі я не хочу сказати, що твоя реалізація не робоча — вона просто для інших сценаріїв і робить акцент ключах та швидкому доступі до них. В той час як контейнери STL повністю абстрагують від цих деталей і дають всі необхідні механізми з коробки.

Честно говоря до сих пор не понял почему нельзя темплейты с++ натянуть на харрей.
Я бы это и сам сделал, просто в моих задачах мне не надо.

до сих пор не понял почему нельзя темплейты с++ натянуть на харрей

Можна, але тобі для цього треба мати:
— універсальну хеш-функцію для довільного типу, або спеціалізовані хеш-функції. Це щоб значення типу K перетворити в uint32.
— використовувати для значень типу T конструктори T(), T(const T&), T(T&&), деструктор ~T() та оператори operator=(const T&), operator=(T&&).
— додати ітератори
— додати підтримку алокаторів

Я бы это и сам сделал, просто в моих задачах мне не надо

Я розумію, що ти вирішував іншу проблему. Але тоді порівнювати з std::map недоречно — порівнюй з int[].

універсальну хеш-функцію

Зачем мне хешфункция ? Зачем ее реализовывать ? С++ темплейты требуют ее реализации ? Можешь заимплементить и вернуть там ноль, я ее никогда не буду вызывать внутри мапы.

Зачем мне хешфункция ?

Щоб значення типу T перетворити в індекс.

Зачем ее реализовывать ?

Щоб користувач твого коду не мав її писати сам для кожного типу.

С++ темплейты требуют ее реализации ?

Темплейти тут ні до чого. Ти порівнюєш з std::map та іншими подібними структурами, а для їх використання все є «з коробки» і мені не треба «написати простенький пул» щоб перетворювати T в uint32.

Щоб значення типу T перетворити в індекс

Зачем ?

Щоб користувач твого коду не мав її писати сам для кожного типу.

Зачем ?

і мені не треба «написати простенький пул» щоб перетворювати T в uint32.

Тебе не нужно писать пул. Я тебе написал врапер у которого инсерт.
insert(char* key, int keyLen, char* value, int valueLen). Сюда можно запихнуть любой объект как ключ и любой объект как значение ровно в одну строчку кода.
Единственное ограничение что ключ и значение не более 900 байт. Но если кто-то реально упрется в это ограничение в своей задаче я потрачу время и сниму его.

Зачем ?

Щоб шукати за ключем виходило швидше ніж O(n). Інакше можна простим масивом скористатися.

insert(char* key, int keyLen, char* value, int valueLen)

Такий код зараз треба писати для кожного можливого типу. В той час як map працює з будь-яким типом ключів і значень для яких виконуються умови згадані вище.

з будь-яким типом ключів

С каким будь яким. Возьмём простейший char*. std::map с ним не работает нормально. Вообще с любым типом который представлен как указатель на объект не работает без 100 приседаний . Стоит ли говорить что в Си указатели чуть менее чем везде

Возьмём простейший char*. std::map с ним не работает нормально.

Працює. Але якщо тобі треба щоб значення порівнювалися як рядки, а не за адресою — тут і треба свій компаратор мапі давати.

с любым типом который представлен как указатель на объект не работает без 100 приседаний

Вказівники дуже легко обертати в якийсь shared_ptr і це вирішує велику кількість проблем. Сирі вказівники — їх краще в С++ взагалі не використовувати.

в Си указатели чуть менее чем везде

Якщо ти зводиш все до С то і з std::map порівнювати не варто. Порівнюй з uint32[] - ця структура буде швидше за твою.

Тебе не нужно писать пул. Я тебе написал врапер у которого инсерт.
insert(char* key, int keyLen, char* value, int valueLen). Сюда можно запихнуть любой объект как ключ и любой объект как значение ровно в одну строчку кода.

Збс, можно, например, запихнуть value типа std::vector? А, подожди, мне его надо к char* кастить? Можно пример кода, который это делает?

Не можешь вектор привести в массив байт, которым он по сути является ?
Погугли.

ты сказал, что можно запихнуть любой объект как значение. Покажи как запихнуть

vector < bool >

(char*)&myVector.

Оно ?
Вектор непрерывный массив байт. Получаем адресс и кастим.

(char*)&myVector.

Оно ?

Мне нравится в каком направлении ты мыслишь, но все же, можно полный пример, который вставляет вектор в твой trie в качестве значения?

Я не могу выполнять роль Гугла за тебя. Сорян. Для меня достаточна аксиома что любой объект в памяти это массив байт. Для харрея этого достаточно. Есть адресс в памяти и количество байт, вставляй ключ и значение.

Т.е. ты понимаешь, что любой объект в твою trie вставить в качестве ключа невозможно, и ты ввел людей в заблуждение? Ок.

Если объект не представлен в памяти как непрерывный массив байт и не может быть преобразован в непрерывный массив байт, то вставить невозможно как ключ. Но я о таких объектах не знаю. Может они у тебя на флопи диске живут в битых секторах, я хз если честно

Объект простой:

    std::vector<bool> my_vector { true, false, true };

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

А если я тебя буду просить 2+2 прибавить. А потом 2+3. А потом 3+2 в четыре утра в моей тайм зоне ?
Можешь как слив засчитать.

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

Будь-який об’єкт типу з інкапсульованими вказівниками чи посиланнями.

До того ж якщо в об’єкті є хендлери (наприклад на файл чи UI-ресурс) то копіювати значення хендлера не вірно — треба заново отримувати ресурс. А зробити це без конструктора копіювання буде дуже складно.

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

Какой поинт в том, чтоб сравнивать структуру, которая не поддерживает range запросы со структурой, которая их поддерживает?

Сравни с вот этой, например:

doc.rust-lang.org/...​TreeMap.html#method.range

Значит ты до сих пор не понял как работает Trie.
Сканы по диапазону есть и они даже эффективней работают чем в TreeMap.

Вот тебе четыре ключа
1 1 1 1 1
1 1 1 1 2
1 1 1 1 3
1 1 1 1 4
При скане диапазона с 2 до 3 харрей прочитает 6 чисел. TreeMap 15.

github.com/...​tKeysAndValuesByRange.cpp

Значит ты до сих пор не понял как работает Trie.
Сканы по диапазону есть и они даже эффективней работают чем в TreeMap.

— Кококо, в хеш-таблице нет скана ключей по диапазону
— Разумеется, нет. Если тебе надо поиск по диапазону, то вот тебе TreeMap, которая позволяет поиск по диапазону
— Кококо, TreeMap это не Trie!

Вот тебе четыре ключа
1 1 1 1 1
1 1 1 1 2
1 1 1 1 3
1 1 1 1 4
При скане диапазона с 2 до 3 харрей прочитает 6 чисел. TreeMap 15.

1. Я не знаю что такое «прочитает X чисел» и почему меня должно это волновать.
2. Твоя trie делает поиск по подстроке? В классической реализации trie поиск возможен только по префиксу, т.е. если тебе нужно где-то найти 2 внутри ключа, тебе все равно прийдется сканировать все ключи от начала до конца.

И в чем смысл твоего поста ?
Хештаблицы работают и медленнее и ничего не ищут по подстройке, но интонация как будто это плюс.
Да, трай ищет только по префиксу ключа. А TreeMap что уже в регекспы без фулскана научился ? В чём твой принт ?
И ещё раз. У меня на гите есть обучающая статья с разноцветными картинками для самых маленьких, чтобы они наконец поняли почему трай сканирует по диапазону лучше бинари три. Да и мой пост с монадами должен был навеять мысли. Ссылку я тебе присылал.

И в чем смысл твоего поста ?

А в чем смысл твоего поста?

Хештаблицы работают и медленнее и ничего не ищут по подстройке, но интонация как будто это плюс.

Не медленнее, а быстрее, бенчмарки уже провели

Да, трай ищет только по префиксу ключа. А TreeMap что уже в регекспы без фулскана научился ? В чём твой принт ?

ты выше намекнул, что твоя Trie сомжет найти 2 внутри ключа без сканирования всех ключей. Если ты пытался что-то другое сказать, сформулируй попонятнее, плиз.

И ещё раз. У меня на гите есть обучающая статья с разноцветными картинками для самых маленьких, чтобы они наконец поняли почему трай сканирует по диапазону лучше бинари три. Да и мой пост с монадами должен был навеять мысли. Ссылку я тебе присылал.

Если прочитать твою «обучающую статью», то читатель как раз прийдет к выводу, что Trie структура сканирует хуже кроме некоторых вырожденных случаев. Чтоб Trie стал маргинально лучше, нужна очень грамотная реализация с кучей нетривиальных оптимизаций, и пока что нет оснований считать, что мы имеем дело с таковой, так как автор не может объяснить свой алгоритм дальше трех картинок из textbook.

Не медленнее, а быстрее, бенчмарки уже провели

Можно ссылку на твои бенчмарки ?

Trie структура сканирует хуже кроме некоторых вырожденных случаев

Можешь объяснить почему хуже ?

так как автор не может объяснить свой алгоритм дальше трех картинок из textbook.

Артёмка, ну какие ещё документы тебе писать. До тебя три разноцветные картинки даже не дошли. Ты только что для себя открыл что трай может искать по диапазону ключей. Это уже прорыв я считаю.

Можно ссылку на твои бенчмарки ?

Ниже в этом топике

Можешь объяснить почему хуже ?

Легко. Книжная реализация Trie «в лоб» будет неэфективной с точки зрения cache locality — в худшем случае при поиске одного ключа будет получать cache miss на каждый символ в ключе без шансов на speculative memory fetch.

Артёмка, ну какие ещё документы тебе писать. До тебя три разноцветные картинки даже не дошли. Ты только что для себя открыл что трай может искать по диапазону ключей. Это уже прорыв я считаю.

Для меня писать ничего не нужно, напиши хотя бы для себя.

Ниже в этом топике

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

Легко. Книжная реализация Trie «в лоб» будет неэфективной с точки зрения cache locality

Книжная реализация на 50 строк кода без патриции и вот этого всего действительно будет неэффективной. Но какое это имеет отношение к моей реализации на 8000 строк кода ?

Для меня писать ничего не нужно, напиши хотя бы для себя.

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

Книжная реализация на 50 строк кода без патриции и вот этого всего действительно будет неэффективной. Но какое это имеет отношение к моей реализации на 8000 строк кода ?

1. Какая разница сколько там строк кода?
2. Я не знаю какое отношение она имеет к конкретно твоей реализации. Ты зачем-то выложил ее в раздел документации в свой гитхаб, а теперь говоришь, что то документация не на твою trie? Выложи документацию на свою trie тогда, будет что почитать.

Выложи документацию на свою trie тогда, будет что почитать.

Открытый исходный код лучшая документация. Бенчмарки лучший арбитр на все твои догадки про кеш миссед. Код дебажится на раз два. При желании можно потратить недельку и во всем разобраться. Можешь начать с HArrayInt. Он вообще простой как табуретка, там не более 300-500 строк кода.

Открытый исходный код лучшая документация.

Документация может быть заинлайнена в исходный код, это сложнее, но возможно. Но даже этого я не наблюдаю, так что нет.

Код дебажится на раз два.

Сенкс, но я дебагером за последние 5 лет пользовался может раза три, и делать это еще раз ради того, чтоб разобраться в корявом С++ коде я не буду.

Документация может быть заинлайнена в исходный код, это сложнее, но возможно. Но даже этого я не наблюдаю, так что нет.

В коде хватает комментариев. Не бойся, глаза боятся руки делают.

дебагером за последние 5 лет

Мсье пишет без багов. Занятно.

дебагером за последние 5 лет
Мсье пишет без багов. Занятно.

lemire.me/...​/i-do-not-use-a-debugger

Linus Torvalds, the creator of Linux, does not use a debugger.
Robert C. Martin, one of the inventors of agile programming, thinks that debuggers are a wasteful timesink.
John Graham-Cumming hates debuggers.
Brian W. Kernighan and Rob Pike wrote that stepping through a program less productive than thinking harder and adding output statements and self-checking code at critical places. Kernighan once wrote that the most effective debugging tool is still careful thought, coupled with judiciously placed print statements.
The author of Python, Guido van Rossum has been quoted as saying that uses print statements for 90% of his debugging.

Это потому что на Линукс дебаг это боль и страдания, по сравнению с вижуал студио. Пускай сами принтами дебажат как в прошлом веке.
Ну и

for 90% of his debugging.

Подразумевает не 3 раза за 5 лет.

Пускай сами принтами дебажат как в прошлом веке.

Ну взагалі то в *ніксах давно є gdb та різні графічні надбудови.

2 Artyom Krivokrisenko, Oleksandr Golovatyi, Punk Floyd

Да здраствуют враперы.
Имплементация которая сохраняет велью произвольной длины
(но не более чем MAX_CHAR-CONTENT_ONLY_TYPE, что не более 900 байт в текущей версии. Не спрашивайте меня почему так, можно пофиксить потом :) )
github.com/...​/HArray/HArrayLongValue.h

Имплементация которая сохраняет value как ValueList в котором каждое значение уникально. Здесь без ограничений.
github.com/...​ArrayUniqueIntValueList.h

Вообще мне нравится ход мысли в проектировании. Тру пацаны теперь используют олдскульные интерфейсы и получают околосветовую скорость. Для ленивых, перекладываем имплементацию листов, алокацию памяти, хранение произвольных строк в велью на харрей используя враперы, которые чуток замедляют харрей, но не критично. Главное что все работает в стройной математической модели.

Упс, мій комент вище був саме до цього.

А хотите пацаны фокус. Меня уже неоднократно просили добавить функциональность, чтобы мапа сохраняла не 32х битные (4 байта) value, но value произвольной длины.

А теперь следите за руками, вы должны прочуствовать всю красоту игры, насладится всем изяществом Trie в имплементации HArray.

Итак, шаг первый.
1. Мы выбрасываем Value, он нам больше не нужен. Выбросить Value в задаче где была необходимость сохранять Value произвольной длины ? Начало неплохое.
2. Далее, в конец ключа мы встраиваем Value и получаем просто более длинный ключ.
Например если ключ 1 2 3 5 а велью 7 8 9 то ключ теперь 1 2 3 5 7 8 9 а велью нет.
3. Поиск ключа это теперь поиск по шаблону,
github.com/...​tKeysAndValuesByRange.cpp
который алгоритмически по скорости равноценен извлечению ключа. Разница в том, что в конце мы сканируем поддиапазон хвостов ключей. Если мы сохраняли одно велью, то поддиапазон будет из одного велью.

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

1. Мы не написали ниодной новой строчки кода. Вин.
2. Мы получили струкутуру которая аналогичная

//если нужно хранить только одно значение, это List с одним значением. Лиспу привет!
map<key with variable len, List<value with variable len>> 
Мапа заимплементила List который теперь хранится как value. А ведь это самый ходовой сценарий для мапы. Банальный индекс это Ключ и список айдишников. Вот это поворот !
3. Но это еще не все. В этот List теперь невозможно добавить дубликат. Более того, проверка на наличии дубликата реализована на скоростях HArray ©
4. А теперь пошла фантастика. Value с одинаковыми префиксами в таком List сжимаются !
Wat ??
5. И удаляются из списка List по всем кананонам, как говорится
dou.ua/forums/topic/33788

Теперь вы понимаете, что хештаблицы и всякие бинарные деревья, по своей задумке просто муссор по сравнению с структурами вроде HArray. Да, так бывает, это как с числом Пи.
2 тыщи лет люди уточняли число Пи через многоугольники. А потом пришел Ньютон и нашел путь просчитать все на листике за несколько дней. Он получил результат, на который у другого ученного ушло 20 лет кропотливых вычислений.

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

PS: Если ты был внимательным читателем, то скорей всего у тебя появился вопрос о коллизиях. Другими словами ключ 1 2 3 4 с велью 5 6, будет таким же как ключ 1 2 3 4 5 с велью 6. Да, есть такая проблема, но она не везде акутальна и может быть решена. Нет ничего невозможного :)

А теперь вопрос на сотню литкод задач.

Если ты был внимательным читателем, то скорей всего у тебя появился вопрос о коллизиях. Другими словами ключ 1 2 3 4 с велью 5 6, будет таким же как ключ 1 2 3 4 5 с велью 6

Как не меняя код и не внося в него изменения решить просто и изящно эту проблему. Мапа у нас хранит только ключи и значения. Этих входных данных для решения задачи достаточно.

ПС: А я говорил что код (почти) идеален. Теперь он часто нами управляет а не мы им. Если у нас есть задача и мы не знаем как ее решить, то это ещё не факт что она уже решена там без нашего ведома ))

А теперь вопрос на сотню литкод задач.

Или на 5 минут для студента 2-го курса толкового вуза, по крайней мере в основных подходах.

Другими словами ключ 1 2 3 4 с велью 5 6, будет таким же как ключ 1 2 3 4 5 с велью 6

Берём условие Фано и понимаем, что если в ключе возможны все значения байт, то вставить новое значение как терминатор не получится. Или у тебя байт 0 не разрешён? Тогда всё банально — просто склеиваются две NUL-terminated строки.
Но если разрешён, то есть два базовых подхода: префикс длины и escape-последовательности. Оба работают. Первый заметно эффективнее, но требует лёгкой модификации функции сравнения. Могут быть вариации типа сборной строки из чанков.
Не хочу лезть в код, смотреть, что ты там сотворил, но если подход не соответствует одному из этих, то можно будет найти нарушение разделения.

Хороший план, но все немножко проще.
Помните я писал

1. Мы выбрасываем Value, он нам больше не нужен.

Разве может быть идеальной картина где есть что-то лишнее ?
Так вот, Value возвращаем. Мы там будем хранить длину нашего Value.

При сканировании добавим проверку и вуаля, коллизий больше нет :)
github.com/...​ray/HArrayLongValue.h#L41

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

ключ 1 2 3 4 с велью 5 6, будет таким же как ключ 1 2 3 4 5 с велью 6

Фиксить это довольно просто. Нужно еще удлинить ключ на один сегмент.
Теперь композитный ключ будет состоять из 3х сегментов.
1 2 3 4 сам ключ + 5 6 велью + 2 длина велью в нашем ключе => Value которое всегда 0.

Получается Value это действительно артефакт в мапе, его роль только терминировать ключ. Сказать в процессе скана ключа что все, наш ключ закончился, переходим к следующему ключу.

Все есмЪ ключ друзья, канонический, триединый из трех сегментов — философский вывод. Пофиксил код врапера.

Теперь композитный ключ будет состоять из 3х сегментов.
1 2 3 4 сам ключ + 5 6 велью + 2 длина велью в нашем ключе => Value которое всегда 0.

Хранение длины в конце это таки красивый трюк, отлично.

Кстате интересный вопрос. Вот мы взяли существенно увеличили длину ключа.
Допустим если ключ был всего 8 байт, а значение было 256 байт, то длина ключа увеличилась до 8 + 256 + 4 = 268 байт. На первый взгляд кажется что это сильно ударит по производительности, с таким то длинным ключем. Но правильный ответ — нет.
Дело в том что больше всего бьют по производительности сильно ветвлящиеся ключи. Например SEQUENCE последовательности ключей, это худший сценарий, где каждый раз мы попадаем почти в одно и тоже место и нужно шунтировать ветку чтобы уйти в другое направление.
Так вот, с длинным 268 байтным ключем, эти ветвления будут только первые 8 байт. Дальше мы выходим на плато и остальные 256+4 байт будут просканированы через обычный цикл for без каких либо джампов по памяти.

Теперь вы понимаете, что хештаблицы и всякие бинарные деревья, по своей задумке просто муссор по сравнению с структурами вроде HArray.

Сори, но нет. Идея выглядела хорошо в начале, но когда дело дойдет до того, чтоб эту поделку использовать, все становится плохо.

Твой код:

	bool getValueByKey(uint32* key,
		uint32 keyLen,
		uint32* value,
		uint32& valueLen)

опустим на время тот факт, что значене не всегда будет кратно 32-битам (или для чего ты uint32* используешь — поясни).

Допустим, я вставил значение длиной 900 байт (опять же, опустим на момент возмущение по поводу того почему именно 900 байт, а не любой размер как стандартные мапы позволяют)

Опустим тот факт, что value — это не тупой набор данных, который всегда можно тупо скопировать.

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

Копирование 900 байт из одного места памяти в другое займет дофига времени. И это только для того, чтоб получить доступ к значению. Мне может быть даже не нужны эти 900 байт, мне нужно только считать одно bool поле из моего значения, или передать ссылку куда-то вниз по стеку. Но даже для этого будут скопированы 900 байт.

Чтоб избежать ненужного копирования данных, все нормальные мапы реализуют API, который возвращает ссылку на значение, которое сохранено внутри мапы. С этой ссылкой можно делать что хочешь, и ее можно передавать куда-то. Часто это ссылка на мутабельное значение, которое можно меня прямо в мапе, что вполне логично — значение в мапах не является имутабельным (в отличие от ключа), и его изменение не приводит к нарушению инвариантов.

На любом бенчмарке от твоей мапы ничего не останется.

Вдобавок, API, который ты запилил для функции — рецепт для выстрела себе в ногу.

	bool getValueByKey(uint32* key,
		uint32 keyLen,
		uint32* value,
		uint32& valueLen)

valueLen — это длина в байтах или в 32-битных словах? Я бы посмотрел ответ на свой вопрос в документации на этот метод, но, как ты сам знаешь, документацию на твою мапу не завезли.

Как твоя мапа знает, что память, которая выделена за *value достаточна, чтоб сохранить все значение? она ничего не проверяет, просто берет и фигачит в память. Если, например, я выделил 100 байт, а твоя мапа попытается записать 900 байт, то память будет похерена — добро пожаловать в неопределенное поведение и уязвимости типа buffer overflow, и это в 2021 году, когда даже в С++ уже завезли возможность писать почти без неопределенного поведения.

Нормальный API принимает на вход указатель на буфер и размер буфера. Внутри проверяют, чтоб не писать за пределы буфера, и, если места в буфере недостаточно, то возвращают ошибку типа ENOBUFS или бросают исключение.

— Кококо, ты не нашел больше до чего доебаться, решил доебаться до проверки на null!
Сорян, ты пишешь громкие слова про конкуренцию с стандартными универсальными структурами данных, причем сливаешь им и по скорости, и по дизайну и по каноничности и безопасности АПИ. Прийдется доебываться до всех трех.

опустим на время тот факт, что значене не всегда будет кратно 32-битам (или для чего ты uint32* используешь — поясни).

Есть враперы. Которые реализуют более конкретные интерфейсы.
Например такие
HArrayChar — Wrapper for HArray with interfaces: char* key, uint32 keyLen, char* value, uint32 valueLen
HArrayUniqueIntValueList — Wrapper for HArray with inteterfaces: uint32* key, uint32 keyLen, List listOfUniqueValues
Можешь написать свои. На любой вкус и цвет. У меня на один врапер уходило минут 15-20.

Копирование 900 байт из одного места памяти в другое займет дофига времени. И это только для того, чтоб получить доступ к значению. Мне может быть даже не нужны эти 900 байт, мне нужно только считать одно bool поле из моего значения, или передать ссылку куда-то вниз по стеку. Но даже для этого будут скопированы 900 байт.

Никто не будет копировать 900 байт, если ты сохранил например 4 или 8 байт. Будут копировать 4 или 8. Да даже если 900 байт ты сохранил, пулы никто не отменял чтобы просто вернуть ссылку на буфер. Запилить пул 50 строк кода, тебе надо ? Я могу прислать, у меня есть. Вообщем как обычно, сам придумал себе проблему, сам героически ее решаешь.

уязвимости типа buffer overflow

Если сделаешь через пул обьектов, оверфлоу не будет. Или можешь добавить эту проверку прямо в врапере, если для тебя это важно. Дело 2х минут.

Прийдется доебываться до всех трех.

Да можешь доеб*ться. Только делай это более профессионально со знанием дела.

Есть враперы. Которые реализуют более конкретные интерфейсы.

Теперь мне еще разбираться с ворохом недокументированных адаптеров к этому trie, потому что афтор не разобрался как сделать каноничный API как сделали на той же std::map?

Никто не будет копировать 900 байт, если ты сохранил например 4 или 8 байт. Будут копировать 4 или 8. Да даже если 900 байт ты сохранил, пулы никто не отменял чтобы просто вернуть ссылку на буфер. Запилить пул 50 строк кода, тебе надо ? Я могу прислать, у меня есть. Вообщем как обычно, сам придумал себе проблему, сам героически ее решаешь.

Это збс. Ты тут целый трактат написал про то как ты хитро придумал хранить большие значения в своей мапе. Но в итоге все свелось к тому же «храните 4 байтное значение, используйте пулы»
www.youtube.com/watch?v=U6Qrw6GdcxQ

Если сделаешь через пул обьектов, оверфлоу не будет. Или можешь добавить эту проверку прямо в врапере, если для тебя это важно. Дело 2х минут.

Скажи мне честно, стоило забивать на тему про швецию ради того, чтоб запилить кучу враперов, которые нельзя использовать по куче причин? Лучше б писал дальше про холодильники и колодец, было прикольнее.

Теперь мне еще разбираться с ворохом недокументированных адаптеров

Сколько можно ныть. Если бы я был таким нытиком как ты, который пасует перед мельчайшими проблемами, я бы такую супер мапу не написал бы даже за 150 лет.
Код врапера, 50 строк кода, который просто перекидует одни интерфейсы на другие.
Что тут сложного ?

Это збс. Ты тут целый трактат написал про то как ты хитро придумал хранить большие значения в своей мапе. Но в итоге все свелось к тому же «храните 4 байтное значение, используйте пулы»

Это особенности реализации. Я не храню ключ и велью как непрерывный набор байт под капотом. Более того, эти самые байты могут быть сжаты префиксной компрессией. Я даже не знаю какой нужно уровень иметь, чтобы не понимать такие вещи и требовать невозможного.
Пул написать 50 строк кода, более того, он уже написан. Я для своих нужд писал. Пул легко можно занести под капот врапера мапы и для клиентского кода вообще ничего не изменится.

которые нельзя использовать по куче причин

Нельзя использовать только у таких специалистов как ты. Которые рисуют себе тысячу вымышленных проблем.

Код врапера, 50 строк кода, который просто перекидует одни интерфейсы на другие.
Что тут сложного ?

Если мне захочется копипастить boilerplate code, я пойду на Go. На С++ такой херней никто не занимается, люди ожидают универсальную структуру с темплейтами, работающую из коробки.

Это особенности реализации. Я не храню ключ и велью как непрерывный набор байт под капотом. Более того, эти самые байты могут быть сжаты префиксной компрессией. Я даже не знаю какой нужно уровень иметь, чтобы не понимать такие вещи и требовать невозможного.

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

ограничениях твоей реализации

Пока я не увидел непреодолимых ограничений.

И задайся вопросом, почему я, чувак, который не осилил С++ и даже уволился с работы, чтоб не иметь дела с С++, должен тебе все это объяснять. Где остальные плюсовики, которые должны все эти косяки спинным мозгом чувствовать и бить тревогу? То ли их нет, то ли они десятой дорогой обходят твой топик.

Потому что нормальные плюсовики пишут в Си стиле, как я. И не морочат себе голову. И эти темплейты по большому счету нахер никому не надо в том виде в котором они есть. Конечно приятно поставить галочку что мапа теперь полностью соответствует интерфейсам std map Но с практической точки зрения в реальном проекте мою мапу использовать даже проще. Не говоря о том что она в 10 раз быстрее работает и более щадящая по памяти.

нормальные плюсовики пишут в Си стиле, как я

Так собі заявка на елітарність. Не можу в С++ — значить правильний С++ обмежується тим, що я можу.

емплейты по большому счету нахер никому не надо в том виде в котором они есть

Як бути з тим, що ці «нікому не потрібні темплейти» у вигляді елементарних vector чи shared_ptr є будь-якому С++ коді складнішому за «Hello world»?

Нормальный API принимает на вход указатель на буфер и размер буфера.

Ну это ж оно и есть?

uint32* value,
uint32& valueLen)

тут уже я что-то совсем не понимаю, что тебе не нравится...

Внутри проверяют, чтоб не писать за пределы буфера, и, если места в буфере недостаточно, то возвращают ошибку типа ENOBUFS или бросают исключение.

И это обычный вариант: в valueLen пишут реальную длину значения, а если она больше переданного на входе — произошло усечение.

Да, значение в виде массива uint32 это странно, он должен сам делать паддинг, по идее. В остальном-то что не так?

Нормальный API принимает на вход указатель на буфер и размер буфера.
Ну это ж оно и есть?

uint32* value,
uint32& valueLen)
тут уже я что-то совсем не понимаю, что тебе не нравится...

github.com/...​HArrayLongValue.h#L80-L95

valueLen используется только для вывода кол-ва прочитанных байт (слов?) из функции. Размер входного буфера нигде не указан и не проверяется, походу ожидается, что можно писать пока память не закончится.

Размер входного буфера нигде не указан и не проверяется, походу ожидается, что можно писать пока память не закончится.

Ааааааааа! Это точно диверсия :((

не надо так. В map нет никаких значений произвольной длины, все значения занимают sizeof(v) байтиков. Посмотри уже наконец, как работает мапа, там несложно

Эээ ...
Это что получается, я могу создать мап только с фиксированой длиной ключа sizeof(k) и фиксированой длиной велью sizeof(v) ? Тоесть я не могу вставлять в один и тотже инстанц мапы ключи разной длины ?

Печаль. Одни из первых версий которые я делал, работали именно с ключами фиксированой длины и работали в два раза быстрее чем текущая версия. Ради фичи вставлять ключи разной длины в один и тотже контейнер (которая полезна в реальных проекта) пришлось свой код замедлить даже больше чем в 2 раза. Я точно помню, даже на слабее машинке 10 млн 16ти байтных рандом ключей вставлял и читал меньше чем за 1 секунду, а сейчас больше 2х секунд. Это конечно все еще в несколько раз быстрее чем std::map, но осадочек остался. Может быть стоит форкнуть версию и сделать версию именно с ключами фиксированой длины в контейнере.

Это что получается, я могу создать мап только с фиксированой длиной ключа sizeof(k) и фиксированой длиной велью sizeof(v) ?
fn main() {
    let mut map1 = BTreeMap::new();

    map1.insert(vec![0, 5, 7, 8], "first");
    map1.insert(vec![42], "second");

    let mut map2 = HashMap::new();
    map2.insert(map1, 123456);
}

Так std::map вроде тоже подобный код поддерживает.
Только потом выясняется что он ссылки на эти обьекты хранит (которые фиксированой длины), а не делает честную побайтовую копию обьектов прозвольной длины и помещает внутрь контейнера.

map1.insert(vec![0, 5, 7, 8], "first");
map1.insert(vec![42], "second");

Но я считаю что у меня это скорей киллер фича. Для СУБД мапа которая хранит ссылки на обьекты в качестве ключей, по сути бесполезна и не может выступать в качестве основного storage механизма. Потому что нормальный storage должен делать копию обьекта произвольной длины и его хранить независимо от внешнего кода.

В коде, который я привел, ключ вставляется в мапу(а не ссылка), мапа владеет ключем, а не содержит ссылку на него. Синтаксис для передачи ссылки отличается.

Вставка ключа внутрь мапы:

    let key = vec![0, 5, 7, 8];
    map1.insert(key, "first");

Вставка ссылки на ключ внутрь мапы:

    let key = vec![0, 5, 7, 8];
    map1.insert(&key, "first");

Ок, значит в Расте мапа работает так как у меня.
Да и в других языках, вроде .NET и Java, такое же поведение.
Поэтому меня и удивило, почему std::map работает иначе, с урезаным функционалом.

Поэтому меня и удивило, почему std::map работает иначе, с урезаным функционалом.

дайте мне пистолет с одним патроном

Ок, значит в Расте мапа работает так как у меня.

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

Может из-за этого ?

1. Очень высокая скорость работы. Тестирование без всяких подвохов, Key/Value контейнер имеет достаточно простые сценарии использования. В среднем HArray в 5-10 раз быстрее работает чем типичные реализации контейнеров вроде std::map, при этом имея даже немного более широкую функциональность. И в многих кейсах работает быстрее чем Hashtables, особенно на скорости вставок. Единственный кейс где Hashtables уверенно обгоняет HArray, это ключи последовательной природы, вроде 1,2,3 и тд. Но ирония в том, что для таких ключей Вам не нужен HArray, не нужен Hashtables или другой map, а подойдет обычный массив.

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

3. Данные в контейнере находятся в сортированом (или близком к этому) виде. Возможность очень быстро определять, есть ли ключ с каким либо префиксом в контейнере. Доступна широкая функциональность по скану ключей. Возможность сканировать поддиапазоны ключей. При этом при сканировании поддиапазона мы не читаем все ключи вподряд, а как бы сканируем только «хвосты» ключей. Такая функциональность недоступна в хештаблицах и доступна в неоптимальном виде в бинарных деревьях.

4. Специфические, но не синтетические, контр примеры. Это когда вставляются длинные ключи с похожими префиксами. Они не использовались в бенчмарках, чтобы не давать HArray явное преимущество, но на этих контр примерах потребление памяти и скорость работы может отличатся в десятки и сотни раз в пользу HArray.

5. Нет хешфункций, нет коллизий. HArray отлично работает с большими обьемами данных, вставить миллиард ключей или больше в такую структуру не проблема. При этом плавное потребление памяти и почти константное время на вставках/поиске, которое зависит скорее от длины ключа, а не от количества ключей.

6. Еще немного плюшек: Возможность задавать правила сортировки ключей внутри контейнера. Нет Stop World событий на вставке (это когда структуре в какойто момент необходимо полностью перестроится, что занимает время). Сериализация/десериализация на диск, на скоростях близких к максимальной скорости работы диска (поскольку структура сбрасывает и подымает на диск страницы памяти, а не делает обход всего графа).

7. Удаление ключей с плавным освобождением памяти и упрощения структуры.

Потому что нормальный storage должен делать копию обьекта произвольной длины и его хранить независимо от внешнего кода.

Ты можешь сделать LSM tree и хранить всё в больших блобах. Вполне рабочий путь даже в оперативной памяти.

я могу создать мап только с фиксированой длиной ключа sizeof(k) и фиксированой длиной велью sizeof(v)

std::map типізована, тобто знає тип ключа і тип значення. І відповідно вміє їх правильно конструювати, копіювати, переміщувати і знищувати.

відповідно вміє їх правильно конструювати, копіювати, переміщувати і знищувати.

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

Это артефакт алгоритма конкретно этой мапы

Это артефакт C++.

как и необходимость имплементить хешфункцию

Для std::map? Держи контекст крепче.

HArray не нужны хешфункции, он никогда не перемещает ключи в памяти целиком.

Я тому і сказав, що це скоріше структура як більше для С підходить, ніж для С++. Ти даєш аналог чогось типу int harray[size] (що, до речі працює швидше ніж твоя реалізація), а на питання про те як вирахувати індекс відповідаєш «напиши простенький пул». В той час як контейнери STL підтримують будь-які типи, правильно роблять копіювання та переміщення, контролюють час життя об’єктів, можуть використовуватися навіть коли інформація про конкретний тип контейнера втрачена (наприклад з ітераторами чи алгоритмами).

правильно роблять копіювання та переміщення

Я так и не понял зачем чтото копировать или перемещать в мапе.
std::map балансирует страницы в красно-черном дереве, перемещает элементы с слота в слот, ну отлично. Это особенности имплементации этой мапы. Мне ничего копировать и перемещать не надо. Я не требую этого от клиентского кода.

не понял зачем чтото копировать или перемещать в мапе

А ось такий приклад:

map<string, string> m1;
string s1 = "key1";
m1[s1] = "value1"; // ключ копіюємо
s1 = "value2";
m1["key2"] = s1; // значення копіюємо

Переміщення знадобиться коли передаємо контроль, робимо swap, реалокуємо буфер і так далі. Потрібно для того щоб не створювати тимчасові копії.

Так я ровно также копирую. Этот пример валиден с харреем. Мне передали буфер я его скопировал в внутренние структуры. Я не храню ссылки на объект.
А вот у std:map проблемы если тип это банальный char*.

Мне передали буфер

В std::map не буфер, а значення типу. А відповідно такі значення правильно конструюються, копіюються та знищуються. Можуть всередині містити вказівники та посилання на інші об’єкти будь-якої глибини.

Как std::map работает с типом char*
Ответ — никак. Он будет хранить 4 байта, адресс в пам’яти а не массив байт.
Это отвечает на вопрос харрей вс стд мап. Сохранить там ключ как набор байт это ещё та головная боль в отличии от.

Як і з будь-яким вказівником на будь-який тип — як з об’єктами типу T* — тобто як з цілочисельними значеннями. Тому запихати вказівники на об’єкти в контейнери вкрай не варто. Для того щоб char* працював у мапі нормально доведеться свій компаратор писати, по мінімуму щось типу:

map m;

Сорі, в коменті вище було трошки коду, але парсер усе порізав. Мало б бути щось таке:

map m<char*, int, strcmp>;

В прикладі вище звісно не «ключ копіюємо», а «значення конструюємо».

Тьфу ты, я думал тут про мотоцикл

Как его запустить? при make у меня куча error вылетает

Пишет что ты криворукий ))))))))

Тебя мама не кормит ? Почему такой злой.

Target: x86_64-linux-gnu
Configured with: ../src/configure -v —with-pkgversion=’Ubuntu 10.3.0-1ubuntu1′ —with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs —enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 —prefix=/usr —with-gcc-major-version-only —program-suffix=-10 —program-prefix=x86_64-linux-gnu- —enable-shared —enable-linker-build-id —libexecdir=/usr/lib —without-included-gettext —enable-threads=posix —libdir=/usr/lib —enable-nls —enable-bootstrap —enable-clocale=gnu —enable-libstdcxx-debug —enable-libstdcxx-time=yes —with-default-libstdcxx-abi=new —enable-gnu-unique-object —disable-vtable-verify —enable-plugin —enable-default-pie —with-system-zlib —enable-libphobos-checking=release —with-target-system-zlib=auto —enable-objc-gc=auto —enable-multiarch —disable-werror —with-arch-32=i686 —with-abi=m64 —with-multilib-list=m32,m64,mx32 —enable-multilib —with-tune=generic —enable-offload-targets=nvptx-none=/build/gcc-10-gDeRY6/gcc-10-10.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-10-gDeRY6/gcc-10-10.3.0/debian/tmp-gcn/usr,hsa —without-cuda-driver —enable-checking=release —build=x86_64-linux-gnu —host=x86_64-linux-gnu —target=x86_64-linux-gnu —with-build-config=bootstrap-lto-lean —enable-link-mutex
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.3.0 (Ubuntu 10.3.0-1ubuntu1)

Main.cpp:854:6: error: variable or field ‘testHArrayStr’ declared void
854 | void testHArrayStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~~~~~
Main.cpp:854:25: error: ‘string’ is not a member of ‘std’
854 | void testHArrayStr(std::string* keys, uint32 countKeys)
| ^~~~~~
Main.cpp:28:1: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
27 | #include “HArray.h”
+++ |+#include
28 |
Main.cpp:854:33: error: ‘keys’ was not declared in this scope
854 | void testHArrayStr(std::string* keys, uint32 countKeys)
| ^~~~
Main.cpp:854:46: error: expected primary-expression before ‘countKeys’
854 | void testHArrayStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~
Main.cpp:992:6: error: variable or field ‘testHArrayStrVar’ declared void
992 | void testHArrayStrVar(std::string* keys, uint32 countKeys)
| ^~~~~~~~~~~~~~~~
Main.cpp:992:28: error: ‘string’ is not a member of ‘std’
992 | void testHArrayStrVar(std::string* keys, uint32 countKeys)
| ^~~~~~
Main.cpp:992:28: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
Main.cpp:992:36: error: ‘keys’ was not declared in this scope
992 | void testHArrayStrVar(std::string* keys, uint32 countKeys)
| ^~~~
Main.cpp:992:49: error: expected primary-expression before ‘countKeys’
992 | void testHArrayStrVar(std::string* keys, uint32 countKeys)
| ^~~~~~~~~
Main.cpp:1180:6: error: variable or field ‘testStdMapStr’ declared void
1180 | void testStdMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~~~~~
Main.cpp:1180:25: error: ‘string’ is not a member of ‘std’
1180 | void testStdMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~
Main.cpp:1180:25: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
Main.cpp:1180:33: error: ‘keys’ was not declared in this scope
1180 | void testStdMapStr(std::string* keys, uint32 countKeys)
| ^~~~
Main.cpp:1180:46: error: expected primary-expression before ‘countKeys’
1180 | void testStdMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~
Main.cpp:1230:6: error: variable or field ‘testDenseHashMapStr’ declared void
1230 | void testDenseHashMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~~~~~~~~~~~
Main.cpp:1230:31: error: ‘string’ is not a member of ‘std’
1230 | void testDenseHashMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~
Main.cpp:1230:31: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
Main.cpp:1230:39: error: ‘keys’ was not declared in this scope
1230 | void testDenseHashMapStr(std::string* keys, uint32 countKeys)
| ^~~~
Main.cpp:1230:52: error: expected primary-expression before ‘countKeys’
1230 | void testDenseHashMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~
Main.cpp:1283:6: error: variable or field ‘testStdUnorderedMapStr’ declared void
1283 | void testStdUnorderedMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~~~~~~~~~~~~~~
Main.cpp:1283:34: error: ‘string’ is not a member of ‘std’
1283 | void testStdUnorderedMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~
Main.cpp:1283:34: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
Main.cpp:1283:42: error: ‘keys’ was not declared in this scope
1283 | void testStdUnorderedMapStr(std::string* keys, uint32 countKeys)
| ^~~~
Main.cpp:1283:55: error: expected primary-expression before ‘countKeys’
1283 | void testStdUnorderedMapStr(std::string* keys, uint32 countKeys)
| ^~~~~~~~~
Main.cpp:1335:6: error: variable or field ‘fillSeqStrs’ declared void
1335 | void fillSeqStrs(std::string* keys, uint32 countKeys)
| ^~~~~~~~~~~
Main.cpp:1335:23: error: ‘string’ is not a member of ‘std’
1335 | void fillSeqStrs(std::string* keys, uint32 countKeys)
| ^~~~~~
Main.cpp:1335:23: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
Main.cpp:1335:31: error: ‘keys’ was not declared in this scope
1335 | void

fillSeqStrs(std::string* keys, uint32 countKeys)
| ^~~~
Main.cpp:1335:44: error: expected primary-expression before ‘countKeys’
1335 | void fillSeqStrs(std::string* keys, uint32 countKeys)
| ^~~~~~~~~
Main.cpp:1352:6: error: variable or field ‘fillRandStrs’ declared void
1352 | void fillRandStrs(std::string* keys, uint32 countKeys)
| ^~~~~~~~~~~~
Main.cpp:1352:24: error: ‘string’ is not a member of ‘std’
1352 | void fillRandStrs(std::string* keys, uint32 countKeys)
| ^~~~~~
Main.cpp:1352:24: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
Main.cpp:1352:32: error: ‘keys’ was not declared in this scope
1352 | void fillRandStrs(std::string* keys, uint32 countKeys)
| ^~~~
Main.cpp:1352:45: error: expected primary-expression before ‘countKeys’
1352 | void fillRandStrs(std::string* keys, uint32 countKeys)
| ^~~~~~~~~
Main.cpp: In function ‘void HArray_VS_StdMap_StrKey(uint32, uint32, uint32)’:
Main.cpp:1370:7: error: ‘string’ is not a member of ‘std’
1370 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~~~~
Main.cpp:1370:7: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
Main.cpp:1370:15: error: ‘strKeys’ was not declared in this scope
1370 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~~~~~
Main.cpp:1370:34: error: ‘string’ in namespace ‘std’ does not name a type
1370 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~~~~
Main.cpp:1370:29: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
1370 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~
Main.cpp:1374:2: error: ‘fillSeqStrs’ was not declared in this scope; did you mean ‘fillSeqBins’?
1374 | fillSeqStrs(strKeys, stopOnAmount);
| ^~~~~~~~~~~
| fillSeqBins
Main.cpp:1379:3: error: ‘testHArrayStr’ was not declared in this scope; did you mean ‘testHArrayBin’?
1379 | testHArrayStr(strKeys, countKeys);
| ^~~~~~~~~~~~~
| testHArrayBin
Main.cpp:1380:3: error: ‘testDenseHashMapStr’ was not declared in this scope; did you mean ‘testDenseHashMapBin’?
1380 | testDenseHashMapStr(strKeys, countKeys);
| ^~~~~~~~~~~~~~~~~~~
| testDenseHashMapBin
Main.cpp:1381:3: error: ‘testStdMapStr’ was not declared in this scope; did you mean ‘testStdMapBin’?
1381 | testStdMapStr(strKeys, countKeys);
| ^~~~~~~~~~~~~
| testStdMapBin
Main.cpp:1382:3: error: ‘testStdUnorderedMapStr’ was not declared in this scope; did you mean ‘testStdUnorderedMapBin’?
1382 | testStdUnorderedMapStr(strKeys, countKeys);
| ^~~~~~~~~~~~~~~~~~~~~~
| testStdUnorderedMapBin
Main.cpp:1390:2: error: ‘fillRandStrs’ was not declared in this scope; did you mean ‘fillRandBins’?
1390 | fillRandStrs(strKeys, stopOnAmount);
| ^~~~~~~~~~~~
| fillRandBins
Main.cpp:1395:3: error: ‘testHArrayStr’ was not declared in this scope; did you mean ‘testHArrayBin’?
1395 | testHArrayStr(strKeys, countKeys);
| ^~~~~~~~~~~~~
| testHArrayBin
Main.cpp:1396:3: error: ‘testDenseHashMapStr’ was not declared in this scope; did you mean ‘testDenseHashMapBin’?
1396 | testDenseHashMapStr(strKeys, countKeys);
| ^~~~~~~~~~~~~~~~~~~
| testDenseHashMapBin
Main.cpp:1397:3: error: ‘testStdMapStr’ was not declared in this scope; did you mean ‘testStdMapBin’?
1397 | testStdMapStr(strKeys, countKeys);
| ^~~~~~~~~~~~~
| testStdMapBin
Main.cpp:1398:3: error: ‘testStdUnorderedMapStr’ was not declared in this scope; did you mean ‘testStdUnorderedMapBin’?
1398 | testStdUnorderedMapStr(strKeys, countKeys);
| ^~~~~~~~~~~~~~~~~~~~~~
| testStdUnorderedMapBin
Main.cpp:1404:11: error: type ‘’ argument given to ‘delete’, expected pointer
1404 | delete[] strKeys;
| ^~~~~~~
Main.cpp: In function ‘void HArray_VS_StdMap_StrKey_Var(uint32, uint32, uint32)’:
Main.cpp:1415:7: error: ‘string’ is not a member of ‘std’
1415 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~~~~
Main.cpp:1415:7: note: ‘std::string’ is defined in header ‘’; did you forget to

‘#include ’?
Main.cpp:1415:15: error: ‘strKeys’ was not declared in this scope
1415 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~~~~~
Main.cpp:1415:34: error: ‘string’ in namespace ‘std’ does not name a type
1415 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~~~~
Main.cpp:1415:29: note: ‘std::string’ is defined in header ‘’; did you forget to ‘#include ’?
1415 | std::string* strKeys = new std::string[stopOnAmount];
| ^~~
Main.cpp:1419:2: error: ‘fillRandStrs’ was not declared in this scope; did you mean ‘fillRandBins’?
1419 | fillRandStrs(strKeys, stopOnAmount);
| ^~~~~~~~~~~~
| fillRandBins
Main.cpp:1424:3: error: ‘testHArrayStrVar’ was not declared in this scope; did you mean ‘testHArrayBin’?
1424 | testHArrayStrVar(strKeys, countKeys);
| ^~~~~~~~~~~~~~~~
| testHArrayBin
Main.cpp:1430:11: error: type ‘’ argument given to ‘delete’, expected pointer
1430 | delete[] strKeys;
| ^~~~~~~

Говорил, что код на марсоход можно ставить, а он даже не собирается.

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

Говорил, что код на марсоход можно ставить, а он даже не собирается.

Ну ошибки нужно читать.
Его компилятор почему-то не видит std::string.
Я добавил явно
#include string в main.
Но вообще-то, в stdafx.h уже есть подобная строчка. И везде такой код компилировался.
Так что хз или поможет. Вполне могут быть проблемы с этой специфической машиной и настройками компилятора на ней.

У меня Убунту обычная и компилятор из коробки.

Его компилятор почему-то не видит std::string.
Я добавил явно
#include string в main.
Но вообще-то, в stdafx.h уже есть подобная строчка. И везде такой код компилировался.
Так что хз или поможет. Вполне могут быть проблемы с этой специфической машиной и настройками компилятора на ней.

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

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

Код я разрабатываю и компилирую в Visual Studio. Потому что удобнее VS ничего не придумали. И уже только потом проверяю под другие компиляторы на Линукс машине. В данном случае код полностью компилировался и работал под Visual Studio на винде, но перестал собираться под Линукс. Что я уже пофиксил.

Код я разрабатываю и компилирую в Visual Studio. Потому что удобнее VS ничего не придумали.

CLion
Code Blocks
Visual Studio Code
Vim

Ты просто VS не видел. Как раз пол часа назад дебажил в Code::Blocks по сравнению с VS он выглядит как текстовый редактор.

Тебе вообще-то первым пунктом CLion назвали.

Ошибки со стрингами пропали, но сейчас вот эти:

g++ HArray_delValueByKey.cpp HArray_getValueByKey.cpp HArray_getKeysAndValuesByRange.cpp HArray_hasPartKey.cpp HArray_insert.cpp HArray_scanKeysAndValues.cpp HArray_rebuild.cpp HArray_resizeHeader.cpp HArray_shrink.cpp HArray_test.cpp Main.cpp -o HArray -w
In file included from HArray_delValueByKey.cpp:20:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from HArray_getValueByKey.cpp:20:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from HArray_getKeysAndValuesByRange.cpp:20:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from HArray_hasPartKey.cpp:20:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from HArray_insert.cpp:21:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
HArray_insert.cpp: In member function ‘bool HArray::insert(uint32*, uint32, uint32)’:
HArray_insert.cpp:1489:2: error: jump to label ‘FILL_KEY’
1489 | FILL_KEY:
| ^~~~~~~~
HArray_insert.cpp:488:12: note: from here
488 | goto FILL_KEY;
| ^~~~~~~~
HArray_insert.cpp:552:10: note: crosses initialization of ‘uint32 keyValue’
552 | uint32 keyValue = key[keyOffset];
| ^~~~~~~~
HArray_insert.cpp:1489:2: error: jump to label ‘FILL_KEY’
1489 | FILL_KEY:
| ^~~~~~~~
HArray_insert.cpp:313:12: note: from here
313 | goto FILL_KEY;
|

^~~~~~~~
HArray_insert.cpp:552:10: note: crosses initialization of ‘uint32 keyValue’
552 | uint32 keyValue = key[keyOffset];
| ^~~~~~~~
HArray_insert.cpp:1493:2: error: jump to label ‘FILL_KEY2’
1493 | FILL_KEY2:
| ^~~~~~~~~
HArray_insert.cpp:539:11: note: from here
539 | goto FILL_KEY2;
| ^~~~~~~~~
HArray_insert.cpp:552:10: note: crosses initialization of ‘uint32 keyValue’
552 | uint32 keyValue = key[keyOffset];
| ^~~~~~~~
HArray_insert.cpp:1493:2: error: jump to label ‘FILL_KEY2’
1493 | FILL_KEY2:
| ^~~~~~~~~
HArray_insert.cpp:361:11: note: from here
361 | goto FILL_KEY2;
| ^~~~~~~~~
HArray_insert.cpp:552:10: note: crosses initialization of ‘uint32 keyValue’
552 | uint32 keyValue = key[keyOffset];
| ^~~~~~~~
In file included from HArray_scanKeysAndValues.cpp:20:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from HArray_rebuild.cpp:20:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from HArray_resizeHeader.cpp:21:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
HArray_resizeHeader.cpp: At global scope:
HArray_resizeHeader.cpp:23:6: error: no declaration matches ‘void HArray::resizeHeader()’
23 | void HArray::resizeHeader()
| ^~~~~~
HArray_resizeHeader.cpp:23:6: note: no functions named ‘void HArray::resizeHeader()’
In file included from HArray_resizeHeader.cpp:21:
HArray.h:28:7: note: ‘class HArray’ defined here
28 | class HArray
| ^~~~~~
In file included from HArray_shrink.cpp:21:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from HArray_test.cpp:21:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
|

^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
In file included from Main.cpp:28:
HArray.h: In member function ‘bool HArray::saveToFile(const char*)’:
HArray.h:213:3: error: ‘errno_t’ was not declared in this scope; did you mean ‘error_t’?
213 | errno_t errorCode = fopen_s(&pFile, path, „wb”);
| ^~~~~~~
| error_t
HArray.h:215:10: error: ‘errorCode’ was not declared in this scope
215 | if (!errorCode)
| ^~~~~~~~~
HArray.h: In member function ‘bool HArray::loadFromFile(const char*)’:
HArray.h:300:3: error: ‘errno_t’ was not declared in this scope; did you mean ‘error_t’?
300 | errno_t errorCode = fopen_s(&pFile, path, „rb”);
| ^~~~~~~
| error_t
HArray.h:302:10: error: ‘errorCode’ was not declared in this scope
302 | if (!errorCode)
| ^~~~~~~~~
make: *** [makefile:2: all] Error 1

Вот так сиди, убирай ворнинги.
Visual Studio ругалась ворнингом, что замените fopen на fopen_s
При этом получается fopen_s с ее структурой errno_t под линукс уже не компилится,
потому что fopen_s ниразу не стандарт.
Получается нужна развилка отдельно под линукс, отдельно под виндовз.
От фикса ворнингов похоже больше вреда чем пользы получилось.

Ладно, доберусь до убунту машины, перепроверю. Чтоб не дергать если еще что всплывет.
Спасибо за труд, хорошо что обнаружилось.

Из доки

Serialize/Deserialize from/to file at a speed close to the max speed of hard drive

HArray ha;

ha.saveToFile("c:\\dump");

ha.loadFromFile("c:\\dump");

Ок. И при чём тут хэш к сериализатору/десериализатору?

Одна из фишек HArray, что при сохранении на диск, он не обходит весь граф обьектов, что может быть очень долго. Он просто сбрасывает страницы памяти на диск и потом ровно также их читает, не нужно вставлять туда по новой элементы по одному. Тоесть если диск пишет, допустим, 250 мб/сек. То примерно с такой скоростью HArray может быть сериализован и десериализован с диска.

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

Получается нужна развилка отдельно под линукс, отдельно под виндовз.

Да, но их таких мест вроде немного.

Стабильность — признак мастерства. За 5 лет ничего не изменилось :)))))

# g++ HArray_insert.cpp
HArray_insert.cpp:1489:2: error: jump to label 'FILL_KEY' [-fpermissive]
  FILL_KEY:
  ^~~~~~~~
HArray_insert.cpp:488:12: note:   from here
       goto FILL_KEY;
            ^~~~~~~~
HArray_insert.cpp:552:10: note:   crosses initialization of 'uint32 keyValue'
   uint32 keyValue = key[keyOffset];
          ^~~~~~~~
я ж правильно понимаю, что если это все-таки сбилдить, и включить все оптимизации, то компилятор может тупо вырезать все ветки, которые ведут к этому гото?

Если не ошибаюсь, это неопределенное поведение.

не шарю c/c++, но судя по цппреференсу, это что-то еще хуже чем ub — стандарт говорит, что такой код даже компилироваться не должен. Что в таком случае оно будет выдавать с О3?

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

LABEL:
uint32 keyValue = key[keyOffset];
goto LABEL;

Я переписал (и закомитил) вот так и претензии у него к коду отпали.

uint32 keyValue;
LABEL:
keyValue = key[keyOffset];
goto LABEL;
goto LABEL;

goto это хорошо, особенно прыгать из одного if в другой. Перепиши по нормальному свой код =)

Его уже нереально переписать.
В 2016 году прилетели инопланетяне, оставили unhumanity logic код, показали землянам как надо писать такие контейнеры и улетели. Остается чесать тыковку и думать, почему это работает.
/Sarcasm Off/

Артем, Панк и Головатый, я думаю у вас достаточно уже занний, чтобы форкнуть эти поделки и дофиксать по человечески, а там уже и монгу потеснить и свой самолет прикупить :-)
Если че я инвестирую свою гробовую заначку :-))))

Мені справді цікаво погратися з цією структурою, але в такому вигляді як вона є це дуже складно. Принаймні для мене.

Trie я ніколи не писав, але колись сам реалізовував різні види дерев з усіма операціями, міряв у порівнянні з std::map та boost::hash_map, навіть підглядав щось у тих всередині. Приємно згадати :)

Если б ТС описал алгоритм, и доказал формально его корректность, можно было бы о чем-то говорить. Портировать алгоритм, который может иметь формальный баг, нет смысла.

Допиливать до рабочего состояния его солянку из С и С++ у меня лично желания нет. Один из этих языков я презираю, а другой — ненавижу.

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

Один из этих языков я презираю, а другой — ненавижу.

Это ты зря. Ценность языка на самом деле не в синтаксисе, а в наличии продвинутого компилятора для него. Люди закопали сотни человеколет, чтобы код после компиляторов Си/Си++ был максимально эффективным. А вот сколько на компилятор Раст потратили человеколет, неизвестно. Но явно меньше чем на Си/Си++.
Отсюда вывод, возможно для Раст лучше залинковать готовые бинарники скомпилированные С/С++ компиляторами.

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

Не вижу смысла об этом спорить. Я свой выбор сделал и в советах не нуждаюсь.

Люди закопали сотни человеколет, чтобы код после компиляторов Си/Си++ был максимально эффективным. А вот сколько на компилятор Раст потратили человеколет, неизвестно. Но явно меньше чем на Си/Си++.

В компиляторе раста такой же бекенд, как и в компиляторе clang. А скоро еще допилят бекенд gcc, тогда вообще збс будет.

Отсюда вывод, возможно для Раст лучше залинковать готовые бинарники скомпилированные С/С++ компиляторами.

Зачем раст линковать с тем, в чем скорее всего течет память и есть UB?

Зачем раст линковать с тем, в чем скорее всего течет память и есть UB?

Пруфы будут ? Вот код который чекает консистентность страниц памяти. Сверяет что каждый бит сошёлся в контрольных суммах. github.com/...​er/HArray/HArray_test.cpp
Этого кода могло бы и не быть, как в тысячах других проектах, но он есть. Его роль, чтобы качество работы программы, хоть на марсоход поставляй. Целый слой логики отвечающий за самодиагностику состояния контейнера в каждый момент времени.

Пруфы будут ? Вот код который чекает консистентность страниц памяти. Сверяет что каждый бит сошёлся в контрольных суммах. github.com/...​er/HArray/HArray_test.cpp

Можешь написать зачем эти биты чекать? Что именно это дает и где этот чек выполняется?

Можешь написать зачем эти биты чекать? Что именно это дает

Типичный баг с этим контейнером выглядит примерно так.
При вставке 7,854,123 ключа, что-то пошло не так. Допустим не дозаписали сегмент в блоке. Постепенно ситуация ухудшается, данные тихо кораптятся и при вставке 9,123,456 ключа процесс крашится или ключи перестают корректно читатся. Вот чтобы обнаружить точку где начались проблемы как можно раньше и существует этот код. Также он чекает висячие ссылки, блоки которые записаны, но не используются и нигде не фигурируют в пулах.

где этот чек выполняется?

Выполняется когда гоняют тесты и бенчмарки. Наиболее тяжелые кейсы, когда контейнер нужно разобрать по кирпичикам при удалении ключей. Начинается сложный процесс описанный в этом топике dou.ua/forums/topic/33788 И этот код помагает чекать, что после каждой итерации при сворачивании от сложного к простому, контейнер находится в полностью консистентном состоянии и готов для применения следующей итерации изменений.

Люди закопали сотни человеколет, чтобы код после компиляторов Си/Си++ был максимально эффективным.

>80% этого закапывания «сохранено» в виде LLVM и бэкенда GCC.

Отсюда вывод, возможно для Раст лучше залинковать готовые бинарники скомпилированные С/С++ компиляторами.

Ніт.

Допиливать до рабочего состояния его солянку из С и С++ у меня лично желания нет. Один из этих языков я презираю, а другой — ненавижу.
Я б портировал на Rust

С любым свеже-выпущенным процессором — производителем процессора поставляется компилятор С.

Кто в здравом уме и не будучи в состоянии наркотического одурения — будет поставлять с процессором компилятор Раста?

Так что, хипстеры могут продолжать развлекаться любимым Растом в своих песочницах и генерить свои «оупен-соурс» неоплачиваемые поделки сколько влезет. Вход в реальную индустрию (тy, что со специалистами и с деньгами) — Расту не грозит.

Можно пример процессора, под который не собирается Rust?

doc.rust-lang.org/...​stc/platform-support.html

Собственно, гарантированная поддержка — лишь для „Tier 1” платформ:
— ARM64 Linux (kernel 4.2, glibc 2.17+) 1
— 32-bit MinGW (Windows 7+)
— 32-bit MSVC (Windows 7+)
— 32-bit Linux (kernel 2.6.32+, glibc 2.11+)
— 64-bit macOS (10.7+, Lion+)
— 64-bit MinGW (Windows 7+)
— 64-bit MSVC (Windows 7+)
— 64-bit Linux (kernel 2.6.32+, glibc 2.11+)

На „Tier 2” собирается, но с „красными” тестами.

На „Tier 3”, может не собраться. Или даже если соберётся — (может) не запуститься (т.к., к примеру, не хватает памяти).

Т.е. внезапно на большинство свежевыпущенных мейнстримовых процессоров есть поддержка или Tier 1 или Tier 2. Это результат годного дизайна экосистемы компилятора — вместо того, чтоб самим пилить бекенд, взяли бекенд LLVM и получили бесплатную поддержку тонны платформ.

Кое-что требует допиливания (поэтому куча вещей или отсутствует, или лежит в Tier 3), но это место, где рынок голосует — put your time where your mouth is. Если не нашлось хотя бы одного красноглазика, который хотя бы запилит Tier 3 поддержку, значит платформа мертва, или настолько закрыта, что несовместима с идеологией Rust.

Скоро допилят gcc as backend, тогда вообще збс будет.

Так что не надо тут сказки рассказывать про «свежевыпущенные процессоры».

Хз как в Раст, но компиляторы на Си/Си++ писали настоящие задроты еще тогда, когда над каждым байтом трусились как над последними штанами. Я когда-то читал что умеет делать Си компилятор, так он умеет даже рекурсию разворачивать в циклы. Тоесть любой новый ЯП, может выигрывать конечно по синтаксису или брать другими фишками. Но если речь идет о том, чтобы написать действительно оптимизированный код, то Си/Си++ компилятор вне конкуренции, поскольку это десятки мегабайт алгоритмов по оптимизации в архиве. И конечно такие невероятные вложения времени и сил для написания компиляторов на новые ЯП уже никто не делает.

Я когда-то читал что умеет делать Си компилятор, так он умеет даже рекурсию разворачивать в циклы.

Я рад, что ты для себя открыл такую штуку как tail call optimization, которую изобрели еще в 70-х годах (как и все остальные крутые штуки в области computer science).

Кстати, есть языки, в которых нет циклов вообще (например, Scheme), и tail call optimization, это единственное, что позволяет в этих языках делать циклы.

Си/Си++ компилятор вне конкуренции

Давно уже не вне конкуренции. Можешь найти бенчмарки, на которых Rust стоит рядом с С и С++.

поскольку это десятки мегабайт алгоритмов по оптимизации в архиве. И конечно такие невероятные вложения времени и сил для построения качественных компиляторов для новых ЯП уже никто не делает.

Умные люди давно поняли, что пилить тулчейн с нуля для каждого языка не получится, поэтому сейчас есть компоненты для построения тулчейны языка, например, LLVM, который используется Clang-ом для компиляции C/C++ и Rust-ом для компиляции Rust кода, предоставляя им практически одинаковые оптимизации (за вычетом оптимизации неопределенного поведения С/С++), и одинаковый набор поддерживаемых платформ.

Кроме того, в пятый раз напишу, что в добавок к LLVM бекенду, уже пилится альтернативный Rust бекенд на базе GCC, и оптимизации и платформы, которые поддерживает GCC, тоже будут доступны в Rust.

Давно уже не вне конкуренции. Можешь найти бенчмарки, на которых Rust стоит рядом с С и С++.

Если бы я написал контейнер который работает по скорости «рядом» с тем же std::map меня бы даже слушать не стали. Сдается мне что мало Расту приблизится к скорости С/С++ чтобы его вытеснить. Поскольку С/С++ это уже давно мейнстрим и стандарт.
Ну а синтаксис, С/С++ выбирают не за синтаксис, а за наработаную кодовую базу и возможность писать на нем низкоуровневый код.

Давно уже не вне конкуренции. Можешь найти бенчмарки, на которых Rust стоит рядом с С и С++.

Раст уже осилил что-нибудь похожее на «placement new»? Насколько я понял, нет.

А без этого, о каких вообще бенчмарках можно говорить?
Разве что, если сравниватъ с кодом, написанным полными нубами (они обычно и пишут эти публичные т.н. «бенчмарки») — которые понятия не имеют, как правильно писать производительный код на C/C++.

А потом, на основании этих т.н. «бенчмарков» — другие нубы рассказывают, что «модерн жаба уже быстрее плюсов».

Раст уже осилил что-нибудь похожее на «placement new»? Насколько я понял, нет.

Это бессмысленный вопрос, потому что в Rust нет конструкторов с понимании C++. Но мы можем написать функцию, которая проинициализирует указанный указатель так, как нам нужно. Возможно нам надо будет привести его через unsafe и всё. Вот пример поэлементной инициализации массива без копирования:

doc.rust-lang.org/...​-array-element-by-element

Но мы можем написать функцию, которая проинициализирует указанный указатель так, как нам нужно.

«Так как нам нужно» — то там где нужно/хочется Расту.
При этом, с выделением памяти (вызовом аллок) под капотом, под каждый чих.

Это не тот код, который может показать перфоманс.

Пример с вектором (Vec<T>) не катит, т.к.
«A vector is a densely packed array in memory, and it requires that all its element occupy the same amount of space. The size of the elements needs to be known at compile time. Trait objects don’t have a known size, so you can’t store them in a Vec.»

То есть, вектор объектов (не указателей на объекты, а именно объектов) — таким не создать.
(хотя, размер класса должен быть известен во время компиляции)

Пример с вектором (Vec) не катит, т.к.
„A vector is a densely packed array in memory, and it requires that all its element occupy the same amount of space. The size of the elements needs to be known at compile time. Trait objects don’t have a known size, so you can’t store them in a Vec.”

То есть, вектор объектов (не указателей на объекты, а именно объектов) — таким не создать.

Покажи пример раст кода, который из-за этого ограничения не скомпилируется.

Обсуждение 2020-го года:

„Why does rust need something like placement new”
www.reddit.com/...​thing_like_placement_new

Насколько я понимаю, текущее положение дел такое:
„the order before optimizing is the following:
— Create object.
— Allocate memory.
— Move object.
And swapping step one and two is difficult to the optimizer.”

В общем, не летает. О каком производительном коде тогда, вообще может идти речь?

Мы вернемся к placement new, ты сначала ответь на вопрос: dou.ua/...​sb_comments_forum#2158644

Мы вернемся к placement new, ты сначала ответь на вопрос: dou.ua/...​sb_comments_forum#2158644
Покажи пример раст кода, который из-за этого ограничения не скомпилируется.

Я не любитель раста, т.к. зачем тратить время на хипстерский язык — который сейчас популярен, а через 2 года о нём никто не вспомнит?
Таких языков всегда было дохрена и раст в этом ряду — далеко не первый и не последний.

То, что я видел — выглядело так:

trait Foo { }

pub struct A {}

impl Foo for A {}

fn test() -> Vec<Foo> 
{
    let generic_vec: Vec<Foo> = Vec::new();
    generic_vec.push(A {});
    return generic_vec;
}

Это должно быть, что-то типа плэйсмент нью (и поскольку Vec<Foo> выглядит, как вектор объектов, а не указателей — вероятно, так и есть).
И это не летает.

P.S. Ок, а такое катит?

pub struct A {}

fn test() -> Vec<A> 
{
    let generic_vec: Vec<A> = Vec::new();
    generic_vec.push(A {});
    return generic_vec;
}
Я не любитель раста, т.к. зачем тратить время на хипстерский язык — который сейчас популярен, а через 2 года о нём никто не вспомнит?

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

То, что я видел — выглядело так:
trait Foo { }

pub struct A {}

impl Foo for A {}

fn test() -> Vec<Foo> 
{
    let generic_vec: Vec<Foo> = Vec::new();
    generic_vec.push(A {});
    return generic_vec;
}
Это должно быть, что-то типа плэйсмент нью (и поскольку Vec выглядит, как вектор объектов, а не указателей — вероятно, так и есть).
И это не летает.

Разумеется, не летает. И это не имеет никакого отношения к placement new.

Trait (на русский переводится как «типаж», чтоб тебе было понятнее) — это не объект, это аналог интерфейса из других языков программирования (или абстрактного класса из C++). Он описывает какое поведение реализует структура, но не описывает какие данные в ней находятся. Из-за этого компилятору и вектору неизвестно сколько байт нужно зарезервировать, чтоб сохранить там данные — 0 байт (как в твоем случае), или 10 байт или 100 байт, или 10 килобайт.

В С++ есть такая же проблема, ты не можешь создать std::vector< IFoo > и класть в него экземпляры классов, котоыре реализует IFoo, потому что в векторе есть место только чтоб хранить сам IFoo, который занимает 0 байт.
Вернее, ты можешь, и вызовешь object slicing, со всеми вытекающими последствиями, когда Rust это запрещает на этапе компиляции, чтоб збс.

Если не веришь, можешь показать код на С++, который делает аналогичное, я тебе покажу где в нем косяк.

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

«Учить» — значит, использовать его для реального кода, т.е. в каком-то проекте. С другой стороны, зачем использовать в проекте какую-то хипстерищну-однодневку — когда есть надёжное и работающее.

С хипстерами я периодически сталкиваюсь (т.к. ими забита вся современная индустрия). Тянет такой в проект какой-нибудь пайтон, непонятно зачем — и пишет на нём ахинею, типа b = bytes(item['value'], 'utf-8') где летят эксэпшены и которую приходится править, чтобы работало.

И спрашивается, что челу мешало использовать язык/средства, в которых он флюент — вмеcто того, чтобы тащить в проект непонятно что и непонятно зачем?
То же самое и с растом.

П.С. Я в предыдущем комменте уже дописал кейс без трэйтoв — такое работает?

Выше тебе уже другой ответил оратор, это делается через MaybeUninit.

но компиляторы на Си/Си++ писали настоящие задроты еще тогда, когда над каждым байтом трусились как над последними штанами

От тех компиляторов ничего не осталось.
GCC прошёл десяток радикальных реформ, одно введение SSA чего стоит.

Ну и часто оказывается, что код после -Os быстрее, чем после -O3, за счёт сокращения объёма и соответственно улучшения кэширования.

Я когда-то читал что умеет делать Си компилятор, так он умеет даже рекурсию разворачивать в циклы.

Это сейчас в базе LLVM.

то Си/Си++ компилятор вне конкуренции, поскольку это десятки мегабайт алгоритмов по оптимизации в архиве.

Всё это в LLVM и бэкенде GCC, повторюсь.
Дело фронтэнда — отдать код в хоть как-то понятном виде, можно без оптимизаций.

Вот пример: по-нормальному надо писать фронтэнд уже с SSA (неизменяемыми значениями), но авторам облом. Они вместо этого на вход LLVM подают набор изменяемых переменных на стеке, а стандартный проход LLVM детектит это и превращает уже в честный SSA.

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

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

dou.ua/forums/topic/33788

Я до речі дивився твій код щоб зробити навколо нього інтерфейс типу мапи, щоб можна було щось типу
HArrayMap<string, string> m;

m["string1"] = std::string("string2");
робити, але без хеш-функції з коробки не вийде. І якщо хешувати об’єкти та контролювати час життя ключів та значень — це доволі багато коду.

Упростил тебе задачу.
Добавил методы врапперы:

bool insert(const char* key, uint32 keyLen, uint32 value);
bool getValueByKey(const char* key, uint32 keyLen, uint32& value);
bool hasPartKey(const char* key, uint32 keyLen);
bool delValueByKey(const char* key, uint32 keyLen);

Теперь чтобы передать std::string можно сделать както так.

std::string myKey = "blablabla";

HArray ha;
ha.insert(myKey.c_str(), myKey.length(), 555);

uint32 value;
ha.getValueByKey(myKey.c_str(), myKey.length(), value);

printf("%u", value);

ha.delValueByKey(myKey.c_str(), myKey.length());

Есть 2 варианта.
1. Написать пулл строк, где хранить строки с айдишниками. А в value хранить айдишник строки.
2. Хранить в value напрямую адресс строки (char*)value. Но такой вариант не подходит для 64х битных систем, value пока что 32 бита, поскольку я в основном использую в своем коде пулы и мне 4,2 млрд ключей и велью с головой.

1. Написать пулл строк, где хранить строки с айдишниками

Якщо їх зберігати як просто масив то час пошуку ключа раптом стане O(n) і мапі будеш програвати.

Якщо ж викорстатит ту саму мапу — в чому сенс тоді ще й твій контейнер брати?

2. Хранить в value напрямую адресс строки

Тут дві проблеми:
1. за різними адресами можуть бути ідентичні рядки.
2. рядок за оригінальною адресою може змінитися.

Що робити з типами які не можна просто скопіювати блоком, а треба викликати конструктор?

Не совсем понял.
Ты можешь на самом деле делать даже так

HArray ha;

std::string myKey = "blablabla1";
ha.insert(myKey.c_str(), myKey.length(), 555);

myKey = "blablabla2";
ha.insert(myKey.c_str(), myKey.length(), 555);

myKey = "blablabla3";
ha.insert(myKey.c_str(), myKey.length(), 555);

Итого у тебя в контейнере 3 ключа.
И также извлекать. Поведение такое же как у обычного Dictionary в какой нить джаве или дот нете.

Ти просто конвертував std::sting в char[555]. Таке дале не з усіма типами можна робити, наприклад якщо інкапсульовані вказівники.

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

struct Bla
{
    int* myValue;
    int* myValue2;
}
Может конвертнуться как ключ в 8 байт. Если ты в одном адрессном пространстве, то вполне себе уникальный ключ.

Якщо є конструктор копіювання то треба викликати його. І деструктор відповідно.

Хто буде робити копії за ось тими вказівниками і хто буде їх звільняти?

Ну Key/Value это аналог индекса в субд. Индекс это отдельная структура для поиска объектов (строк) по определенному ключу. Индекс строится рядом оригинальной структуры (таблица) и не участвует в удалении или создании объектов (строк) в ней.

Все так, але я ж те саме вже кілька разів повторив. Щоб скористатися твоїм контейнером в С++ коді треба:
— написати свою хеш-функцію, можливо для кожного типу даних
— знати як правильно робити копії ключів та значень в/з масиву байтів
— знати коли ключі та значення звільняються, конвертувати їх в правильний тип (інформація про який вже буде втрачена) і викликати деструктор

До того ж щоб просто замінити std::map у найпростішому випадку мені треба буде:
— написати ітератор для кожної можливої комбінації типу ключа та значення
— додати підтримку алокаторів.

Якщо говорити про чистий С то твій контейнер там має більше сенсу, але і порівнювати з мапою тоді не варто — мапа дає набагато більше функціоналу і робить більше.

Давай я как-то доберусь и сделаю value произвольной длины и тогда можно будет повторить полностью интерфейсы std::map.

Ось тут хороші приклади та посилання на документацію про те який мінімум треба зробити для свого stl-like контейнера — stackoverflow.com/...​ng-your-own-stl-container.

не надо так. В map<k,v> нет никаких значений произвольной длины, все значения занимают sizeof(v) байтиков. Посмотри уже наконец, как работает мапа, там несложно

sizeof(v)

Сейчас у меня sizeof(v) всегда 4 байта.
А сделать нужно чтобы можно было велью не только 4 байта записывать.

эх, думал тут про мотоциклы...А тут скукотень для которой и boost’а хватает.

Продолжаем тему с тестированием Артема, где Артем не смог вставить 80 млн ключей.
dou.ua/...​rums/topic/18849/#2044301

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

На своей машине в твоем тесте я вставил 1.2 миллиарда ключей

По памяти у меня отожрало 18.3 гб.
(Оригинальный размер датасета:
1.2 billion * (4byte key + 4 byte value) = 9.6 гб)
Если у тебя на борту больше, сможешь вставить больше, вплоть до 4 млрд ключей.

Круто. Как только теперь это можно использовать на практике?

Це ідеальний код без багів який перемагає будь-що за будь-яких умов. А на практиці користуйтеся своїм попсовим std::map та іншими недолугими подєлками з тестами, документацією та білд-процесом.

Любой код идеален, пока к нему не добавляют тесты, документацию и билд-процессы.

Любой код идеален, пока

... його не запускають

У меня есть знакомый который работал в конторе которая занимается видеокодеками, писал как раз те самые автоматизированные тесты. Как ты думаешь, как они тестируют свой код ? Да очень просто. У них есть большой корпус не сжатых видео файлов самых разных типов и размеров и они их пропускают их все через видеокодеки, а потом смотрят что получается. Полный цикл их тестирования длится часами. Они не встроены в какието билд степы и тд.
Ровно также как и архиваторы тестируют. Пакуют корпус больших файлов, потом разжимают и смотрят что побитово все совпадает. Понимаешь, никто не пишет такие простые тесты «А что будет если ноль передали в качестве параметра». В этом нет смысла. Для таких проектов, с сложными алгоритмами, это не имеет смысла.

Так что ты можешь сколько угодно рассказывать что есть баги, но я тебе говорю как тестирую я и как тестируют подобный код в других конторах. А ты просто ввиду отсудствия опыта работы над такими проектами, пытаешся натянуть свое понимание «юнит тестирования ради юнит тестирования».

Ну и я жду код, который крашит или показывает баги в моей либе. Пока я такого кода не увидел. Но зато ты на каждом углу трезвонишь что «мало тестов есть баги». Что не соответствует действительности.

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

Це інтеграційні та/або E2E тести.

Они не встроены в какието билд степы

А юніт-тести є частиною білд процесу.

ты можешь сколько угодно рассказывать что есть баги

Ти можеш скільки завгодно розказувати, що багів нема.

Ну и я жду код, который крашит или показывает баги в моей либе

А я чекаю тестів.

Це інтеграційні та/або E2E тести.

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

Ты не согласен ? Ты собрался код архиватора или кей велью тестировать монкей юнит тестами типа а что будет есть неправильный ключ передали ? Твой подход просто не работает. А недостаток квалификации не дает тебе понять почему это не будет работать. Лол.

Коли у тебе є коробка радіодетальок то в процесі збірки радіо з них:
— перевіряємо кожну детальку — юніт-тести
— перевіряємо кожен вузол (блок живлення, антену, підсилювач, ...) - інтеграційні тести
— перевіряємо, що можна покрутивши ручку посунути стрілку на потрібну частосу і почути передачу саме на цій частоті — E2E тести.

Те що ти пропонуєше це зібрати радіо, а коли щось не працює то лізти перевіряти кожну детальку. Що і дорого, і довго, і показує незрілість процесів.

— перевіряємо кожну детальку — юніт-тести
— перевіряємо кожен вузол (блок живлення, антену, підсилювач, ...) - інтеграційні тести
— перевіряємо, що можна покрутивши ручку посунути стрілку на потрібну частосу і почути передачу саме на цій частоті — E2E тести.

Ну отличный пример. Тебе показывают радио, которое работает, ловит в жмеринке австралийские радиостанции, а ты кричишь что в этом радио тебе не хватает проверки того как работают транзисторы. И на основе этого делаешь глубокомысленный вывод что «либа сырая без тестов». Тебе самому не смешно ? Тебе показывают рабочуют либу которая прошла все E2E тесты, на загрузке миллиардов ключей, что по сути является сертификацией ее качества. А ты предлагаешь тестирование отдельных резисторов, транзисторов и диодов. Ты натестируешь, все по отдельности будет работать, а вместе не будет работать. В этом нет никакого смысла. Тебе надо, можешь добавить такие тесты, я апрувну пулл реквест, но в моем понимании это пустая трата времени.

Тебе показывают радио

Твоя ліба в найкращому випадку вузол. Сама по собі вона нічого не робить і має бути використана в реальній програмі щоб мати для мене користь. І цілком нормально очікувати, що був хоч якийсь контроль якості і відповідності заявленим характеристикам.

вывод что «либа сырая без тестов»

Ліба не викликає довіри і не має сенсу дивитися на неї доки не буде тестів.

Тебе показывают рабочуют либу которая прошла все E2E тесты

Де ці тести?

А ты предлагаешь тестирование отдельных резисторов, транзисторов и диодов.

Так, мені потрібні гарантії того, що базові компоненти належної якості і мають заявлену поведінку.

В этом нет никакого смысла.

В цьому лише сенс і є. Брати непойми що у використання, без тестів та документації — який в цьому сенс?

Тебе надо, можешь добавить такие тесты

Тобі треба щоб я серйозно сприймав твою лібу і спробував її використовувати — ти добавляй тести.

E2E тест имеет наивысшую ценность. Он показывает что продукт работает, не отдельные компоненты его, а весь продукт работает так как надо. Все части согласованы в своей работе.
Е2Е тест для контейнера, это вставка большого количества ключей и их правильное извлечение и удаление.
Нагрузочные тесты в 1600 строчек кода которые тестируют три метода insert/get/delete ты можешь найти в функции main.
Ты можешь запустить эти тесты. Можешь написать свои тесты и взять свои сценарии использования. Известных багов на сейчас — нет. Хотя сценарии использования запредельные, превысили миллиард ключей.

E2E тест имеет наивысшую ценность.

Вартість, а не цінність. Бо коли щось, що можна було зловити на етапі білду ловить E2E — це дуже дорого, з великим відставання і під час роботи над іншою вже задачою.

Известных багов на сейчас — нет.

За відсутності тестів так завжди буває.

Вартість, а не цінність. Бо коли щось, що можна було зловити на етапі білду ловить E2E — це дуже дорого, з великим відставання і під час роботи над іншою вже задачою.

Мне не дорого. Мне нормально. Кейс Артема я пофиксил за минут 15 и даже не залазя в код написал что пул закончился.
Любой баг в своем алгоритме я фикшу не более 2х часов. Мне главное найти сценарий, где мой код не отработает. И именно Е2Е тест наиболее замороченный и эффективный для выявления багов.
А юниттесты в моем кейсе (не в других проектах) почти бесполезны.

Мне не дорого. Мне нормально.

Але ж ти намагаєшся продати свою лібу не лише собі. Відповідно і виникають питання про відповідність заявленого реальності.

Я никому ничего не продаю. И даже не собираю пожертвования. Я просто говорю что я написал достаточно революционный алгоритм и подкрепил его открытым кодом, Е2Е нагрузочными тестами и гарантией отсутствия багов. В случае наличия багов (а если они не обнаружились при работе с миллиардом ключей в контейнере, то в обычном кейсе на неск миллионов ключей и говорить неочем) я пофикшу их очень быстро.

Я никому ничего не продаю

«Продаю» тут в сенсі не заробляння грошей, а пропозиції іншим скористатися твоїм кодом. Бо інакше для чого ця тема?

Дуже легко зробити репо з випадковим кодом і без тестів, а потім казати, що тести не потрібні і все ідеально — бо гарантія.

Е2Е нагрузочными тестами

Де покриття коду можна подивитися?

гарантией отсутствия багов

Як перевірити цю гарантію. На слово вірити не буду. Які тести можна запустити і побачити, що код робить те, що від нього очікують?

Які тести можна запустити і побачити, що код робить те, що від нього очікують?

Запускаешь проект и ждёшь минут 15 прохождения всех тестов.

Запускаешь проект

Що це словосполучення означає?

У тебе там написано Windows & Linux. Ок:

git clone github.com/Bazist/HArray.git
cd HArray
make

вгадай що далі.

Мейк файл вроде как чинил. Специально Линукс машину для этого искал чтобы потестировать. Не сбилдидось ?

На вінді:

> nmake
NMAKE : fatal error U1064: MAKEFILE not found and no target specified
Stop.

На лінуксі:

$make
make: *** No targets specified and no makefile found.  Stop.

Так вот мейкфайл
github.com/...​ob/master/HArray/makefile

Ты в правильной директории make запускаешь ?

Ты в правильной директории make запускаешь ?

Чітко слідую інструкціям в документації.

Ок, скорей в твоём случае

git clone github.com/Bazist/HArray.git
cd HArray/HArray
make

Тобто від документації могла б бути якась користь?

Ок, а як щодо купи варнінгів в «ідеальному коді»? — pastebin.com/mcrWHkVD

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

Да и смотрю ворнинги в основном ругаются на дебажные printf

Ворнинги ничего не значат в данном случае

Відсутність тестів теж нічого не означає. Як і відсутність документації. Та і взагалі може не компілятися — це мало що значить, код же ідеальний. Ось і гарантія.

Корректность кода тестируют здесь более основательными методами

Тобто ніякими.

Ну і для чого такий коди без тестів, без документації та ще й з варнінгами? Ти за той час поки намагаєшся мене переконати в ідеальності свого коду вже давно міг покриття хоча б 70% зробити. І не на словах, а прямо в репорті покриття показати — ось воно.

Код идеальный багов нет, потому что те драконовские нагрузочные тесты с кучей разных проверок все давным давно повычищали. Чай не автопилот теслы тестируем, а простой контейнер для ключей. Он покрыт тестами на 100% вдоль и поперек.

Код идеальный багов нет

Але це не точно — нема ніяких тестів щоб це підтвердити.

Он покрыт тестами на 100%

Але це не точно — репорт з покриттям відсутній.

та ще й з варнінгами

Ты бы те варнинги почитал хотябы. На printf ругается. Очень смешно.

Ты бы те варнинги почитал хотябы.

На даному етапі мені достатньо того що вони є. Після цього вірити в те що «код ідеальний» якось не раціонально.

Ты бы те варнинги почитал хотябы.

Та хоч на scanf — компілятор тобі явно каже, що твій код не ідеальний як мінімум.

E2E тест имеет наивысшую ценность. Он показывает что продукт работает, не отдельные компоненты его, а весь продукт работает так как надо.

Как бы не имеет. Это тест, в котором под капотом десятки или даже сотни компонентов, который показывает pass/fail сигнал. Если pass — все збс. Если fail — хуево, и чо теперь с этим делать? Надо проверять компоненты по отдельности и разбираться какой именно из них косячит, сейчас посмотирм тесты более низкого уровня, oh shit...!

Полный цикл их тестирования длится часами.

Вот когда нет юнит тестов, циклы тестирования и занимают часы и даже недели в случае если это ручное тестирование. Юнит тесты должны выполняться за считанные минуты (и тут счёт на тысячи тестов будет).

Но ты прав в одном — end-to-end тест с правильными данными способен покрыть 100% кода, если конечно в коде нет сложных превентивных проверок на например инъекции в данные.

Но ты прав в одном — end-to-end тест с правильными данными способен покрыть 100% кода, если конечно в коде нет сложных превентивных проверок на например инъекции в данные.

Именно так и есть. Только таким тестам можно доверять. Юнит тесты, да, можно добавить просто как демонстрация какой-то функциональности, но не более.

Юнит тесты, да, можно добавить просто как демонстрация какой-то функциональности

Ти схоже просто не розумієш для чого потрібні юніт-тести. Бо сказане звучить як «компіляцію можна додати як демонстрацію, але не більше».

потрібні юніт-тести.

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

Я тебе уже сказал как тестируют видеокодеки и архиваторы

А я тобі сказав чому не варто витрачати час на лібу у якої навіть юніт-тестів нема.

Пойди продай им свои юниттесты.

Для чого це мені? У них є продукт який задовольняє, або ні мої вимоги як користувача. Як вони це роблять там у себе і чи тестують взагалі мене не хвилює.

Але ж ти намагаєшся продати не продукт, а код, лібу навколо якої треба ще буде писати продукт. І відповідно виникає питання які гарантії якості, де хоча б тести і покриття?

кадр который сделает упор на простенькие юнитесты, никогда не напишет и не отладит такой сложный проект.

Зі «складним проектом» ти сильно вперед забігаєш. Спочатку хоча б юніт-тести додай.

Зі «складним проектом» ти сильно вперед забігаєш. Спочатку хоча б юніт-тести додай

Ну ты человека который собрал работающее полностью радио пытаешься убедить сидеть с амперметром и чекать резисторы, диоды и транзисторы что они работают. Зачем ? Если радио работает в самых замороченных сценариях очевидно же что все его мелкие части тоже работают как надо.

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

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

Для себе можеш робити як хочеш, але якщо плануєш мене зацікавити використовувати свій код то маєш показати тести і покриття хоча б.

радио работает в самых замороченных сценариях очевидно же что все его мелкие части тоже работают как надо

У тебе не радіо, а усього лише один з його елементів. Сама по собі твоя ліба не дає ніякої цінності користувачам доки навколо неї не написано код програми. А мені щоб використати твій компонент потрібні гарантії роботи відповідно заявленим характеристикам.

Ти ж пропонуєш зібрати радіо з твоїм компонентом і потім перевіряти чи працює воно, а як ні — лише тоді перевіряти твій компонент. Усе якось догори ногами і в самий затратний спосіб.

Ти ж пропонуєш зібрати радіо з твоїм компонентом і потім перевіряти чи працює воно, а як ні — лише тоді перевіряти твій компонент. Усе якось догори ногами і в самий затратний спосіб.

А ты как хотел ? Если у нас на работе хотят использовать Редис, то запихивают его в наш сценарий использования, именно с нашими данными и смотрят как он себя ведёт. С какой скоростью работает.
Я могу лишь гарантировать что мой код корректно работает на всех моих нагрузочных сценариях и известных багов не обнаружено, даже если вставляли сотни миллионов и даже миллиард ключей.

у нас на работе хотят использовать Редис

Продукт з багатьма відомими випадками використання, з документацією та підтримкою. Не соромся вже, порівнюй свою лібу з віндою чи лінуксом, там теж мабуть тестів не пишуть через свою крутість (ні).

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

А, так у тебе ліба спеціально лише для гарантованих тобою сценаріїв. Якісь інші дані чи послідовність операцій і все, гарантія втрачається?

А, так у тебе ліба спеціально лише для гарантованих тобою сценаріїв. Якісь інші дані чи послідовність операцій і все, гарантія втрачається?

Дело в том, что контейнер по своей сути достаточно прост для тестирования. Он даже не многопоточный. Просто пишешь ключ и велью и читаешь. Не понимаю почему ты такую проблему раздул. Например DniproDB это полноценная база данных, там багов много, есть ограничения, ее тяжело тестировать. Сценариев много использования, многопоточность и вот это вот все.
Но key/value это самая надёжная часть. Она действительно без багов и я в ней уверен.

контейнер по своей сути достаточно прост для тестирования

І де тести?

Не понимаю почему ты такую проблему раздул

Тому що є певний когнітивний дисонанс коли хтось стверджує про ідеальний код без багів і в той же час не може зрозуміти для чого потрібні юніт-тести.

і в той же час не може зрозуміти для чого потрібні юніт-тести.

Так ты же сам расписал эволюцию тестов.
Самое простое пройти юнит тесты.
Сложнее интеграционные.
И самое сложное Е2Е.

Я пропустил юнит тесты потому что они ровно нихрена толком в моем случае не выявят. Но выявит тяжёлая артиллерия вроде интеграционных или е2е, типа чекать контрольные суммы, лить ключи до аут оф мемори, лить ключи самой разной природы и длины, чекать каждый вставленный ключ и тд.

Так ты же сам расписал эволюцию тестов.

Яка ще еволюція? Я тобі розписав для чого потрібні види тестів які використовують найчастіше.

Я пропустил юнит тесты потому что они ровно нихрена толком в моем случае не выявят

Крім випадків коли виявлять і не треба буде дебажити код щоб знайти елементарну помилку чи тайпо.

Крім випадків коли виявлять і не треба буде дебажити код щоб знайти елементарну помилку чи тайпо.

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

Элементарных помилок там уже давно не осталось

Це треба довести.

Будем руководствоваться презумкцией невиновности. Багов нет до тех пор пока не доказано обратное.

:D

Тоді і правда будь-який код без документації та тестів є ідеальним та без багів.

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

Ты занимаешься тем что выдумываешь себе проблемы. Ворнинги, тесты не такие, документация не такая на контейнер с тремя методами и тд. А что если контейнер у тебя не будет работать. У меня работает на миллиарде, а у тебя наверно план 50 млрд ключей вкачать.

Короче не занимай время, я спать.

Розумієш, ти прийшов продавати свій «ідеальний код без багів», а коли до нього виникли питання почав вигадувати виправдання замість виправити. За той час, що ти намагаєшся довести, що юніт-тести чи документація не потрібні ти міг би їх вже написати.

І я особисто не бачу ніякого сенсу навіть пробувати користуватися лібою в якій мало того, що відсутні тести та документація, так ще і автор на всі вказані проблеми доводить що вони не важливі.

Навіть уявимо хтось ризикнув твоїм кодом скористатися, включив його в свій білд. Бац — варнінги, білд падає. Всьо, на цьому твого «ідеального» коду позбуваються і забувають про нього.

Я до речі дивився твій код щоб зробити навколо нього інтерфейс типу мапи, щоб можна було щось типу

HArrayMap<string, string> m;

m["string1"] = std::string("string2");

робити, але без хеш-функції з коробки не вийде. І якщо хешувати об’єкти та контролювати час життя ключів та значень — це доволі багато коду.

Але маючи таке можна було б вже порівнювати на тих сценаріях де мапу та інші колекції використовують.

але без хеш-функції з коробки не вийде

А зачем хешфункция, если HArray не требует хешфункций ? В этом его фишка, он сортирует и упаковывает ключи без всяких хешей, что является предметом нестабильности в хештаблицах (коллизии).

Можно сделать конвертацию как-то так. Правда строка должна быть выровнена по 4м байтам.
std::string str = «bla bla bla»;
uint32*key = (uint32*)str.c_str();

В этом его фишка, он сортирует и упаковывает ключи без всяких хешей, что является предметом нестабильности в хештаблицах (коллизии).

Если я оберну твой harray в некий ipc процесс, сделаю маршаллинг для выборки ключей (для потоко защищённости и множества клиентов), то так как

uint32*key = (uint32*)str.c_str();

Ключ у тебя поинтер на сырые данные, и каждый новый запрос будет аллоцировать вычитанные из сокета ключи в новое место в памяти — harray не сможет детерминированно вычитать ключ foo с разных запросов?

Не совсем понял схему. HArray нужно передать три значения
поинтер на ключ uint32*, длину ключа и велью. И он прочитает все данные с этого поинтера и вставит в контейнер.

Не совсем понял схему. HArray нужно передать три значения
поинтер на ключ uint32*, длину ключа и велью. И он прочитает все данные с этого поинтера и вставит в контейнер.

Я еще не разбирался с твоим проектом построчно =)
Но если есть два ключа
std::string key1="foo"
std::string key2="foo"

Они идентичны? Даже если c_str() указывает на разные «foo» ?

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

Контейнер копирует ключ и проверяет дубликаты

Як він це робить якщо у нього нема інформації про тип ключа, а відповідно і про те як правильно зробити копію. Наприклад у мене ключем буде наступний тип:

struct key {
   void* pv;
   char* pc;
   ...
};

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

Я копирую массив байт

В С++ цього не достатньо. Я там спеціально написав вказівники. Тобто мені ще треба буде вручну робити копії усіх об’єктів всіх можливих типів.

Але навіть не це найгірше. Як ти будеш викликати деструктор для «масиву байт»? Ось ті вказівники які для твого коду насправді байти є адресами пам’ять за якими треба звільнити.

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

Ты мне передал допустим обьект как ключ 64 байт к примеру.
Я их себе в контейнер скопировал.

Деякі з тих байтів були вказівниками чи вкладеними об’єктами які треба правильно копіювати.

После того как ты ключ из контейнера удалил, я в контейнере 64 байт удалил.

І проігнорував деструктор типу, не звільнив пам’ять.

А зачем хешфункция

для того щоб наступне працювало:

sting s1 = "key1", s2 = "key1";

if (m[s1] == m[s2]) {...}

А з твоїм кодом мені треба:
— написати свою хеш-функцію
— десь зберігати ключі
— десь зберігати значення
— відстежувати час життя ключів та значень

Мапа та інші контейнери все це роблять з коробки.

Не совсем понятно. Этим кодом ты будешь сравнивать values

m[s1] == m[s2]

Если ты написал простинький пул, где велью это айди строк в этом пуле, то никаких проблем писать такой код нет.

Если ты написал простинький пул, где велью это айди строк в этом пуле,

Поки забудемо про те що мені ще й «простенький пул» писати треба буде і спитаю — як цей пул організувати? Просто масивом/списком, чи може std::map використати?

Убрал все Warnings. Можно забирать master.

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

ПС: По ходу дела допустил одну очепятку и тесты это мигом выявили, исправил.

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

Можно уточнить в каких проектах?

В 2 моих проектах (полнотекстовый движок и документоориентированная субд)
и еще пару человек мне писало по имейлу, спрашивало что да как.

Можно ссылочки?

А где используются сии поделия, в каких продуктах?

Наконецто. А почему ты пятницу пропустил ?

Сделал ресайз пулов динамически и залил изменения в master

І тест додав який підтверджує, що тепер не буде крешитися за таких умов?

Добавлять тест на вставку 1,2 млрд ключей ?
А у std::map такой тест например есть ?
Потому что у меня на таком обьеме он крашится и виснет.

Добавлять тест на вставку 1,2 млрд ключей ?

Як інтеграційний тест — чому б і ні?

А у std::map такой тест например есть ?

Я тобі вже показував де подивитися які юніт-тести є для мапи.

Як інтеграційний тест — чому б і ні?

И что это даст ? А если у когото память закончится ? И почему именно 1,2 млрд ключей. 2 млрд я не смог вставить, у меня на ноутбуке память закончилась. Может лучше 8 или 16 млрд ключей ? Чтобы каждый юзер кричал что моя либа крашится, не разобравшись про аут оф мемори эксепшин в тестах ?

И что это даст ?

Це дасть підтвердження того, що код працює як і до змін.

А если у когото память закончится ?

У кого це «кого-то»?

И почему именно 1,2 млрд ключей.

Не знаю чому ти саме таку кількість запропонував.

Может лучше 8 или 16 млрд ключей ?

Може і краще. Ти як автор тесту маєш розуміти який саме сценарій перевіряєш.

Потому что у меня на таком обьеме он крашится и виснет.

А ты эксэпшены ловишь?

Ловлю, но в этом мало смысла. Это Fatal Error который по хорошему приведет к перегрузке приложения. (пример с out of memory, если память закончилась, то делать уже ничего не получится)

Добро пожаловать на СУбд-строительные верфи. Господин Прокофьев пока не подтвердил свою подписку на следующую пятницу. Ты ещё можешь успеть запрыгнуть на алтарь.

Он регулярно лечится водичкой, но видать вода не живая, а мертвая :-)

Мде. Если в эмигосрачах у тебя ещё был шанс пресмыкаться из под табуретки, то в техническом топике просто проедуться катком по твоей тонкой вайтишной натуре )

Это опять какая-то дичь про лабрадора??? :-)

візьмем цю моду винесення в топчік
ти дорікаєш std::map за відсутність

6. Сохранятся на диск на уровне скорости работы дисковой системы — не умеет

і відповідно вихваляєшся наявністю цього в тебе
dou.ua/...​rums/topic/18849/#2136000

Якщо оце та геніальна функція збереження github.com/...​ster/HArray/HArray.h#L220 —

Велью фиксировано действительно 4 байтами, потому что чаще всего это поинтер на объект.

то скажи, будь-ласка, яка користь із файла з поінтерами на об’єкти, стрічки, будь-що, якщо це поінтери. Де збережені дані?

Вы просто не понимаете рассовое превосходство моего алгоритма. Это просто другой уровень, чем гавеные деревья и хештаблицы. И прежде всего даже не по скорости, а по функциональности.

«ві нє понімаєтє, єто другоє»

6. Сохранятся на диск на уровне скорости работы дисковой системы — не умеет

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

Мне это удается, поскольку я сбрасываю на диск готовые пейджи скопом. Если у тебя какая-то говноструктура, которая представлена миллионом мелких объектов в графе который ещё нужно обойти, то твоя структура нихера не оптимизирована для работы с диском. Мой же контейнер сериализируется/десериализируется на уровне 100 мб/сек с обычным шпиндельный диском.

Мой же контейнер сериализируется/десериализируется на уровне 100 мб/сек с обычным шпиндельный диском.

Ты сейчас хвастаешься или жалуешься?

Конечно хвастаюсь. У меня готовые мегабайтные страницы памяти пишутся на диск. И также читаются. Получить такое в обычной мапе или хештаблице совсем не просто. Собственно поэтому они и не умеют сбрасываться и подыматься с диска на максимальной скорости винчестера.

то скажи, будь-ласка, яка користь із файла з поінтерами на об’єкти

Я написал на HArray два движка СУБД, которые работают с диском.
В качестве value сохраняется offset в файле.

Сама по себе твоя идея переложить сериализацию на контейнер черевата. Ты никогда не сможешь решить общий случай сериализации объекта, со всеми вложеностями, циклическими ссылками и тд. Это не тривиальная задача. В дотнете есть мембервайсклон. Но и он нихера не работает, а бежит по верхам. Потому мне твой бомбеж на счёт велью не понятен.
Ты пытаешься решить задачу которая за скобками Кей велью контейнера

Сама по себе твоя идея переложить сериализацию на контейнер черевата. Ты никогда не сможешь решить общий случай сериализации объекта, со всеми вложеностями, циклическими ссылками и тд. Это не тривиальная задача. В дотнете есть мембервайсклон. Но и он нихера не работает, а бежит по верхам. Потому мне твой бомбеж на счёт велью не понятен.
Ты пытаешься решить задачу которая за скобками Кей велью контейнера

«а как дисал, а как дисал»

зверху в топіку

Легендарный супероптимизированый алгоритм, который реализует весьма сложные Trie структуры данных и топчет вот эти вот эти ваши самые реализации ассоциативных массивов std::map, glib, dictionary.net и прочью заморскую попсу в разы, а то и на порядок.

Ты услышал что тебе написали ?
Как ты в дикшинари собрался сериализовать например объекты с циклическими ссылками, а потом десериализовать чтобы все встало в памяти как надо автоматом ?

Ты услышал что тебе написали ?
Как ты в дикшинари собрался сериализовать например объекты с циклическими ссылками, а потом десериализовать чтобы все встало в памяти как надо автоматом ?

а ти побачив чим ти хвалишся і що насправді у тебе?

Ты приписываешь дикшинари и мапам функциональность, которую они _впринципе_ не могут реализовать правильно. Кстате это хороший был бы вопрос на собеседование, узнать у кандидата почему сериализовать десериализовать объекты в общем случае нифига не тривиальная задача. Думаю всяких профнепригодных слоупоков можно было бы с лёгкостью отсеивать. О таком в этих ваших литкодах не напишут. А теория графов тут просто кричит

Ты приписываешь дикшинари и мапам функциональность, которую они _впринципе_ не могут реализовать правильно.

serde.rs
Out of the box, Serde is able to serialize and deserialize common Rust data types in any of the above formats. For example String, &str, usize, Vec, HashMap are all supported. In addition, Serde provides a derive macro to generate serialization implementations for structs in your own program.

common Это примитивные типы. Они мне нахер не нужны в субд. А ты реализуй в общем случае, чтоб любой объект. Пример с циклическими ссылками я приводил.

common Это примитивные типы.

serde вполне работает с композитными типами

Пример с циклическими ссылками я приводил.

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

Потому что я не вижу смысла это реализовывать как половинчатое решение. А в всяких дикшинари и мапах оно именно так и работает. Вплоть до того что мы сериализовать ниче не будем пока не заимплементите сами функцию как ваш объект правильно сериализовать. Если так, то какая разница где его сериализовать. И зачем это делегировать мапе.

И зачем это делегировать мапе.

Это хороший вопрос. Зачем сувать всю возможную функциональность в один класс? Есть такая штука как single responsibility principle. Один класс отвечает за сериализацию, другой за эфективное хранение данных в памяти и организацию доступа к нему и т.п.

Вплоть до того что мы сериализовать ниче не будем пока не заимплементите сами функцию как ваш объект правильно сериализовать.

Это логично, не? Ты как разработчик библиотеке общего назначения можешь не знать каких-то особенностей сериализации какого-то кастомного объекта, поэтому вполне логично, что фреймворки сериализации предоставляют возможность указать как именно нужно сериализировать кастомный тип данных.

Кстате кинь ссылку на исходники функции которая сериализирует хешмап в расте.
Если там сбрасываются готовые страницы (для этого там должен быть рукопашный менегер памяти) то Ок, если там обходят циклопический граф и сериализирует элементы по одному то тормознутая академическая имплементация.

Кстате кинь ссылку на исходники функции которая сериализирует хешмап в расте.

github.com/...​rde/src/ser/impls.rs#L374

Если там сбрасываются готовые страницы (для этого там должен быть рукопашный менегер памяти)

Тебе для этих целей не нужен рукопашный «менегер памяти», тебе нужен memory-mapped file.
У тебя этого, разумеется, нет, ты просто открываешь файл через fopen и фигачишь туда свои внутреннее представление данных.

если там обходят циклопический граф и сериализирует элементы по одному то тормознутая академическая имплементация.

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

А точно ли в сотни?
Вот рассмотрим вырожденный случай когда элемент один байт и они раскиданы по всей памяти таким образом, что чтение никогда не будет происходить из кеша.
А в качестве сторедж пусть будет ssd на pcie.

Это Артем ошибся примерно на два или три порядка. Обход десятков миллионов объектов разбросанных по памяти в классическом академическом варианте Node(Parent) может быть очень и очень больно. На счёт SSD немного не согласен, не обязательное условие. SSD будет выигрывать прежде всего за счёт отсутствия позиционирования механической головки. Но если мы в лоб пишем гигабайты ровно по дорожке диска, то там скорость на обычном шпиндельном диске может достигать до 500мб/сек. Именно с такой (Макс) скоростью HArray весь сериализируется и десериализируется с диска. Что очень удобно для старта СУБД, она просто подымает страницы памяти с диска в рам, не вставляя элементы по одному в контейнер по новой.
И здесь же элементарно реализуется уже известный LRU, ибо отслеживать какие нужны страницы в рам а какие лучше вытеснить на диск тоже очень просто.

Обход десятков миллионов объектов разбросанных по памяти в классическом академическом варианте Node(Parent) может быть очень и очень больно.

Если твоя задача это сериализация хеш структуры, то в мапу подставляется свой аллокатор который выделяет память под ноды из своих собственных «страниц» памяти из адресного пространства процесса, и дальше эти страницы можно дампить на диск как хочется через fwrite. Если имеет значение порядок данных, то есть некая связанность — это тоже в аллокаторе можно разрулить (грубо говоря где выделять место под ноду)

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

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

. Но если мы в лоб пишем гигабайты ровно по дорожке диска, то там скорость на обычном шпиндельном диске может достигать до 500мб/се

Да ладно!
с хардваре видимо знаком по наслышке ?

Одиночный HDD 7200 пишет 250MB/s на внешних дорожках в лучшем случае. На внутренних скорость падает вдвое.
То есть в среднем диск будет выдавать 160MB/s
и это в идеальных условиях.
например www.ixbt.com/...​exos-x18-18tb-review.html

Да ладно!
с хардваре видимо знаком по наслышке ?

ні, просто бенчмаркає у вакуумі, без зливання кешів

Ждал ответа про о enterprise 10/15k rpm, но видимо и так достаточно lol

В Дельфі колись серіалізація-десеріалізація компонент на формах (а потім і датамодулях) була зроблена. І навіть працювала.

Про серіалізацію підграфу об’єктів я диплом писав :)

Дауж, мысли в слух. На самом деле этот топик не о супер оптимизированных алгоритмах, а о человеческой психологии. Я не являюсь экспертом в большом количестве компутер сцаинц технологий. Мне нечего сказать человеку который написал крутую нейросеть, например. Но почему-то другие люди, приходят в этот топик и пытаются показать свою крутую экспертизу и непременно через несколько сообщений садятся в лужу.
Дружище, каким бы ты не был спецом, литкодовцем, топ архитектором, миллионером и просто хорошим человеком пойми. Есть области где ты не шаришь и тебя ждёт фиаско.
Поэтому впредь, если у тебя нет признанных собственноручных крутых реализаций хотябы хештаблиц, других очень сложных структур данных, движков баз данных, не трать мое и свое время. И не подымай эту тему.
Спасибо.

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

Перше — «визнаність» твоєї «крутої реалізації» дещо надумана. Знають її лише на тих ресурсах де ти сам її піарив, та і то знають більше через твій стиль спілкування, а не через крутість реалізації.

Друге — не треба бути автором «крутих реалізацій» щоб задавати питання. А питання до твого коду є — чому він не компілиться без ворнінгів, чому крешиться, чому в нього треба лазити руками кожного разу коли треба щось «оптимізувати», чому розкидані магічні значення, ...

Третє — ти перший почав «піднімати цю тему» та «витрачати час» і мав би бути елементарно вдячним за те, що люди витрачають час і дивляться на твій код. І не просто дивляться, а знаходять проблеми, задають питання по темі та висловлюють сумніви.

И не подымай эту тему.

Після того як ти припиниш розказувати нісенітниці про ідеальний код та те як твоя реалізація удєлує мепи та хеш-таблиці.

Я признаю если человек приходит и спрашивает, понимая что у него редкая возможность пообщаться с экспертом в этой области, которых наверно можно пересчитать поштучно.
Но если какой-то х*й с горы приходит с гонором про умнее всех и начинает мне с умным лицом фейлить и факапить с анализом, максимум на что он способен найти какую-то косметику на уровне джуна, то имею слабость такого кадра приземлить на землю. Извините если кого обидел.

Я признаю если человек приходит и спрашивает, понимая что у него редкая возможность пообщаться с экспертом в этой области, которых наверно можно пересчитать поштучно.
Но если какой-то х*й с горы приходит с гонором про умнее всех и начинает мне с умным лицом фейлить и факапить с анализом, максимум на что он способен найти какую-то косметику на уровне джуна, то имею слабость такого кадра приземлить на землю. Извините если кого обидел.

з тобою через твою геніальність напевне в тімі ніхто не працює :-), ти сам собі тім

У порядних конторах про косметику тебе просто засміють.
Твій код може бути геніальним (на самом деле нет), але при PR review там є банальні, автоматичні, жесточайші і бездушні automatic checks.
1. компілюється з warnings — до побачення, помилка, приходь, коли пофіксаєш
2. cyclomatic code complexity check — функція на 1300 рядочків — до побачення, помилка, приходь, коли пофіксаєш
3. security check — лазаєш по пам’яті грязними ручонками, конвертуєш int/pointer направо і наліво — будь любєзєн, внеси ці зауваження в exception/ignore list (ну, тобто всю твою програму) — до побачення, помилка, приходь, коли пофіксаєш
і т.д.

І це ще до того, як колеги почнуть твій код розглядати.

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

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

це все adhominem і мимо каси, ліда блін, бггг

хомячковиє стандарти, бггг

«алгоритмы уровня кернела» — ти хоч кернел бачив (Windows/Linux)? Тебе би Linus змішав з гівном, або просто проігнорив би за такий код

Я буду использовать гоуту, длинные функции и прочий арсенал настоящего джедая который пишет алгоритмы уровня кернела

Я сам код «рівня кернела» не писав, писав навколо нього, але довелося його побачити і навіть трошки подебажити у певній кількості (Windows 7/8). І код той написано чисто (з використанням SAL — docs.microsoft.com/...​quality/understanding-sal) і компілиться він без варнінгів. І покрито його тестами теж дуже добре.

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

Якби не хочеться переходити на особистості, але ти переоцінюєш свою експертність та унікальність.

Код чого тобі показати? Це ж не я в цьому топіку розказую про ідеальність свого коду.

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

Именно так. Есть реализация, есть о чем говорить. Есть экспертиза. Нет реализации — давай досвидания.

Я рассказываю про идеальность своего кода, поскольку он хорошо работает

Варнінги при компіляції, segfault під час робити та необхідність ручних правок просто для того щоб зберігати дані іншого розміру — це що завгодно, але не ідеальний коди і не код який добре працює.

segfault під час робити

Последний сегфолт я видел когда память закончилась, когда пытались вкачать 300 миллионов ключей. Вот бросал ссылку на машинках помощнее вкачивали миллиард ключей без сегфолтов.
Причем на таких объемах всякие стдмапы и денсы дохнут как мухи, но не мой алгоритм. Ибо ему пох* на коллизии он создан на запредельные объемы данных.

Це круто, але історії лишаються історіями. І доки у тебе не буде тестів та прикладів використання вірити їм буде не кожен.

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

Хуцпа

«Хуцпа» — определяется как «особо циничная, подлая, наглая ложь», «верх цинизма и наглости, парализующий оппонента».
Другие определения, которыми часто обозначают понятие «хуцпа», — сверхнаглость, бессовестность, бесцеремонность, нахальство, хамство, нетерпимость по отношению к другим, дерзкое и наглое лицемерие. Часто эти определения употребляют с приставкой «сверх-» и другими словами, подчеркивающими превышение всяких норм и выход за рамки установленного: «сверхнаглость», «суперложь», «экстраординарная наглость», «необыкновенное нахальство», «неслыханное вранье», «невиданная дерзость», «запредельная бесстыжесть», «небывалая бесчестность» и так далее.
...
Американские судьи Алекс Козински и Юджин Волок приводят объяснение употребления этого слова в английском языке на примере анекдота: «Юноша, который убил обоих родителей, чтобы получить наследство, в суде просит о помиловании, ссылаясь на то, что он круглый сирота».

===
по решті пунктів Oleksandr Golovatyi сказав нижче — dou.ua/...​rums/topic/18849/#2135965

Аффтор, а ты случайно раньше не зажигал на sql.ru в ветке ПТ?
уж больно ник и слог знакомый

и на sql.ru и на ixbt жег напалмом под ником bazist.

точно, помню что на ПТ был базист (в линке аффтора на гитхаб увидел bazist и сразу вспомнил) и тоже всякую дичь нес
я вот хотел проверить свою мысль и зашел на sql.ru — выяснилось что ПТ уже почил в бозе, не выдержал бедняга срачей :)))

да ну, там же вменяемое сообщество, гениев не приемлют :)))

Похоже мы все в колбасных топиках имели дело с проффесиональным троллем ))

из него тролль как известно из чего пуля. Он искренне верит в свои поделки.

Это и есть искусство быть троллем — на серьёзных щщах задвигать всякую чушь и убедить всех что ты сам в неё веришь. :))

Это «чушь» работает. Так что пецтеть можно что угодно, но на гите все есть и запускается ровно в 2 клика.
И после этого все вопросы кто тролль, а кто дело говорит, моментально отпадают.

тут увы другйо случай: для троллинга несколько лет педалить какую то говноподелку — чересчур.

Так я не понял. Ты в очередь на жертвоприношение величайшим алгоритмам становится будешь ?
Если честно, для меня шкурка галерного архитектора наименее ценна. Все знают что нормальный архитектор грамотно борется с сложностью в проекте, а галерный архитектор наоборот, вносит максимальное количество своей глупости, чтобы раздуть проект до максимально возможных сроков. Как по штату сотрудников, так и по срокам реализации.

:) бомбеж оценил. Но по сути то есть что сказать? За 5 лет получилось внедрить свою поделку хоть куда то, хоть в приватбанк продать?

Я понимаю что основная твоя работа это торговать лицом.
Но нет. Ты ошибся топиком. Это чисто технический топик.
Так что на следующую пятницу ты именно технически должен быть подкован,
а не как менеджер продажник.

вопросов больше нет :)))

Да я. Но нужно учитывать что оба ресурса российские.
И на обоих ко мне отношение было предвзято политическое.
Особенно на

ixbt

где в технических темах
я общался с отморозками которые спонсируют ДНР/ЛНР.
Именно поэтому я ушел с обоих этих форумов.

Шо опять? Там ошибки в коде, автора кто только(в том числе и я немного) ссаными тряпками не гонял :)

Ты немного опоздал. Для своих жертвоприношений самым совершенным алгоритмам я беру не больше одной тушки мамкиных архитекторов / прЫнциплов в неделю. Но ты всегда можешь записаться в очередь.

как успехи? За 5 лет сколько процентов рынка бд подмял? Кому из лидеров рынка свое поделие продал? А хоть той, ошибки в коде на которые тебе 5 лет назад пофиксил? :)

Нет там никаких ошибок в коде. Успокойся уже. Нагрузочные тесты все прекрасно показывают. Были тесты (не я делал) миллиард ключей вставляли.

На счёт рынка, если ты хочешь торговать, гоу в ларек с шавермой. Это теоретически-практические работы в компьютер сцаинц.

а їсти ти також будеш

компьютер сцаинц

?

Это мое хобби, могу себе позволить

Были тесты (не я делал) миллиард ключей вставляли.

В 32х битовом процессе при том что под винду доступно только 2Gb виртуального адресного пространства на процесс.

docs.microsoft.com/...​ed/virtual-address-spaces

/LARGEADDRESSAWARE

+ Структура частично занимается компрессией данных
+ Никаких проблем пересобрать под 64 разряда

/LARGEADDRESSAWARE

Ну будет 3Gb, ты даже не вместишь туда миллиард uint32_t. Как всегда спиздел про миллиарды, даже не подумав.

+ Никаких проблем пересобрать под 64 разряда

Проблемы я тебе выше указал, оно работать у тебя не будет.

Ну будет 3Gb,

Не 3 а 4 Гб. Учи матчасть.

Проблемы я тебе выше указал, оно работать у тебя не будет.

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

Не 3 а 4 Гб. Учи матчасть.

Точно-точно? Есть такое подозрение что ты ошибаешься :)

И если немного подумать, то вот как будет в 32-битном процессе работать взаимодействие с кернелом если юзерспейс отдать 4gb?

Если округлить будет почти 4. Кернел не отъедает гиг, ест мелочь на адрессные таблицы для win32.

Google говорит что эта опция даёт до 3gbyte.
Ты уверен что знаешь как округление работает?

Наверно у меня какой-то другой Гугл
This is why the /LARGEADDRESSAWARE flag was introduced into the Portable Executable (PE) file format header. The LAA flag indicates to the operating system that this 32bit application is „certified” by its developers to handle full 4GB of memory.

codekabinett.com/...​handle full 4GB of memory.

Тебе перевести?
Этот флаг говорит системе что процесс может работать с полными 4gb.

По твоей же ссылке

To understand what this means we need to go back in time. When the Windows operating system was 32bit only, it was able to address a maximum of 4GB of RAM memory (theoretically, effectively only ~3GB). Any application instance running on 32bit Windows was only allowed a maximum of 2GB of memory. The remaining memory was reserved for the operating system to run its own processes and other applications.

32-битный процесс в 32-битной системе никаким образом не может получить 4gb.
3 вот твоя цифра.

32-битный в 64-битной я без понятия. Я не знаю как работает MMU в этом случае, например можно ли держать два поддерева страниц в одном процессе, одно с 32 а другие для кернела с 64. Подозреваю что нет, и ограничение все равно будет 3gb.

Но я не специалист, а ты же специалист по округлениям, расскажи как это работает.

Но я не специалист, а ты же специалист по округлениям, расскажи как это работает.

Старт адрессов в заголовке Win32 (PE32) процесса начинается с 400000. Тоесть тебе доступно 4гб — 400кб.

По факту, оси нужно еще меньше места в твоих 4гб.
Представь что тебе нужно написать кернел операционной системы для 32х бит. Все хорошо, потому что ты можешь хендлить все 4гб в поинтере, все тебе доступно. Но есть проблема. А что если твое приложение должно взаимодейстовать с ОС ? На какие адресса ты должен отправлять колы внутри своей песочницы ? Для этого, когда тебе выделяют память тебе ее выделяют не всю, а чуть-чуть оставляют как раз на маппинг внутренних вызовов функций к внешним. Те самые 400кб. Другими словами когда процесс дергает win32 функцию, он дергает свой внутренний адресс, а таблица уже указывает что код который должен выполнятся, находится за рамками песочницы в 4гб.

Пришёл адрес в кернел из юзерспейса. Каким образом кернел получит доступ к данным по адресу?

Кернел насколько я понимаю всегда замаплен в адресное пространство юзерспейса.

Пришёл адрес в кернел из юзерспейса. Каким образом кернел получит доступ к данным по адресу?

Для кернела твой процесс не более чем песочница в контейнере на 4 гб виртуальной памяти.

Кернел насколько я понимаю всегда замаплен в адресное пространство юзерспейса.

Да. Кернел знает, что если ты обращаешся внутри своей песочницы на адресс меньше 400000 то ты по сути дергаешь экстернал кол Win32 api. Именно поэтому первые 400 килобайт своего адрессного пространства тебе не доступны.

Нет, подождите.
Вот пошёл сисколл с адресов буфера в кернел. Этот буфер надо отправить отправить в сетевую карту.
Как и где ты его будешь копировать?

Что значит как и где ? Для Кернела все страницы памяти открыты для чтения. Кернел видит, что пришел указатель на буфер с такойто песочницы, лезет туда (вычисляем этот адресс: начало адресса процесса + адресс буфера) и читает все данные.

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

Кернел на то и кернел, что он стоит НАД всеми песочницами. Он ими управляет и для него нет никаких защит памяти на страницах. Эти правила существуют для песочниц. Как получить точный адрес буфера я написал. Адресс процесса + Адресс буфера который пришел с песочницы процесса = Физический адресс в ОЗУ

Здрасьте приехали.
Давай ты сначала разберёшься как на низком уровне это работает.

Давай ты сначала разберёшься как на низком уровне это работает.

Нет, давай уж ты разбирайся.
Поскольку это работает, буферы передаются с процессов на сетевые карты и тд. А как это работает, это уже тебе разбираться, с моими обьяснениями или без.
Лучше без.

Я тебе говорю как.
Оно замаплено, все замаплено одновременно. И кернел и юзерспейс.
Просто дескрипторы которые кернел помечены как доступные из привилегированного режима только.
И вот делаем сисколл и передаём указатель в кернел. Кернел можем обращаться к этому указателю как к своему.

вычисляем этот адресс: начало адресса процесса + адресс буфера

1) адреса
2) Что такое «начало адресса процесса»?

Именно поэтому первые 400 килобайт своего адрессного пространства тебе не доступны.

1. 0×400000 это 4MiB, а не «400 килобайт». Мелочь, но в данном контексте существенно.

2. Первые 4MB пропускаются для удобства ядра, которое туда может мапить, например, первые 1MB физической памяти, даже не давая процессу доступа к ним.

3. В x86 дизайне сами каталоги страниц должны лежать в той же памяти. На них вместе с сопутствующими структурами ядра тратится до ~5%, это ~200MB от 4GB.
Если бы эта была какая-нибудь SystemZ, там такого ограничения нет. Но x86 тупее.

4. Если процессу отдано 3.8GB, то чтобы ядру хоть что-то сделать полезное — придётся выкидывать страницы процесса на диск, работать, а потом закидывать обратно. Работа дорожает в разы. И вообще при 1.5GB физической памяти уже рекомендуют всем переходить на следующую ступень (64 бита), иначе возня со всякими double bounce buffers замедляет всё и вся.

4. Если процессу отдано 3.8GB, то чтобы ядру хоть что-то сделать полезное — придётся выкидывать страницы процесса на диск, работать

Все верно, но конкретно в моем случае это обычное консольное приложение, которое максимум выводит что-то на консоль. Простыни экстернал либ не импортирует, потому может использовать вполне все 3.8 гиг в 32х битном процессе

Все верно, но конкретно в моем случае это обычное консольное приложение, которое максимум выводит что-то на консоль.

Из-за него всем прочим придётся подвинуться и ужаться, во главе с ядром.

В общем, это аццкая перверсия и пользы с неё ноль. 64битку не зря придумали.

of RAM memory (theoretically, effectively only ~3GB).

Это приблизительное слишком приблизительное.
Сам подумай. Если ОСи нужно АЖ 1 гб из твоего адрессного пространства, то как раньше работали машинки на винде с 64 мегабайтами ОЗУ на борту. И это НА ВСЕ. По факту оси нужно отьесть с твоей памяти на кернел совсем крохи.

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

. Если ОСи нужно АЖ 1 гб из твоего адрессного пространства, то как раньше работали машинки на винде с 64 мегабайтами ОЗУ на борту.

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

Читать научись контекст. Физически оси нужно намного меньше гига с твоего адрессного пространства.

Какой именно?
Ты в курсе что ОС тоже растут и жрут память. Драйвера там всякие.

Что-то меня уже начало утомлять.
Вернемся к первоначальному посту. Почему я считаю что 4гб (округленно) а не 2гб или 3гб как писал Горчак и о том, в какую сторону я округляю.

ibb.co/F3jDDpD

Ось одала больше чем 3,8 гб с флагом /LARGEADDRESSAWARE

Дальше уже разбирайтесь с Горчаком наедине. Ты сверху Горчака, или Горчак сверху тебя. Это уже как вам там интересно.

Фу таким быть

Ну извините. Кстате тебе плюс, за адекватное общение и попытку разобраться. Надеюсь Прокофьев к следующей пятнице начистит отполирует шкурку (хотябы копнет по верхам trie). Я на своем алтаре к следующей пятнице хочу увидеть по крайней мере ухоженную тушку галерного архитектора.

И что там случилось в конце, любопытства ради? Исключение или ты на ноль проверить забыл?
А sizeof указателя точно 4 байта?

Точно 4, x86 сборка
Дальше краш приложения, нужно экскпшин в c++ ловить

Ну значит порядка 200 мегабайт ОС на твоей системе. Сделать какой-нибудь драйвер который полгига зааллоцирует и все.

2Gb сделали по умолчанию не просто так. Есть shared memory, есть file mapping, создание потоков и стеков для каждого потока. Для каждого действие необходио линейное виртуальное адресное пространство. Место под всё это резервируется ОС. Всё равно тебе эту память никто не даст для работы, не зависимо от того что ты там считаешь или насчитал.

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

ibb.co/F3jDDpD

Отдает 3.8 гиг из коробки

Ну т.е. ОС всё равно забрала себе 300Mb, а не 400Kb. И при этом ты не можешь выполнить ни одного системное действия, ни замапить расшаренную память, ни даже создать поток, ни открыть динамически DLL. Такое себе достижение. Именно поэтому этот ключь не дефолтный, он подразумевает что разработчик понимает что он делает.

Ну а теперь вернёмся к миллиардам (именно во множественном числе) записей? Опять знаменитое бубно-сжатие пространства для бетоно-метров, теперь уже для памяти процессов под виндой? :D

Ну а теперь вернёмся к миллиардам (именно во множественном числе) записей? Опять знаменитое бубно-сжатие пространства для бетоно-метров, теперь уже для памяти процессов под виндой? :D

Я же писал. Не я даже тестировал. У меня были канадские архитекторы из Белл на ставке QA

Читай.
forum.ixbt.com/...​c.cgi?id=26:43056:545#545

К слову там жалуются что удаление у меня не реализовано. И я потом его таки реализовал. И как реализовал. Это настоящий алгоритмический шедевр.

Я же писал. Не я даже тестировал. У меня были канадские архитекторы из Белл на ставке QA
Читай.
forum.ixbt.com/...​c.cgi?id=26:43056:545#545

З таким самим успіхом можеш перечислити регалії Artyom’а і Oleksandr’a — з переписуванням їхніх контор — Amazon, Microsoft, GoDaddy, Tableau і т.д. (про мої контори скромно умолчім, я анонім і ніхто)

І як ти відповідаєш тому QA? Нєбось подякував?

forum.ixbt.com/topic.cgi?id=26:43056-20
Ні, написав тому прославленому архітекту
«Вы меня уже начинаете утомлять своей тупостью.»

А, он оно чьо :-)

Насколько я понимают все это будет в адресном пространстве процесса, в тех двух гигабайтах.

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

Насколько я понимают все это будет в адресном пространстве процесса, в тех двух гигабайтах.

Да, просто нужно различать caging который имеет стандартная библиотека и в основном она тусуется в нижних 2Gb и системные вызовы, которые имеет доступ к любому виртуальному адресному пространству процесса.

Потом, гуглится что ещё покрывается знаковым интом.

Именно так, весь фан начинался когда использовалась арифметика указателей, а указатели оказывались знаковыми. Слишком много легаси кода не работало. Например скомпиленное Win32S приложение из 3.11 должно было работать вплоть до Windows 7 в режиме совместимости.

Второй момент named shared memory. Например, «базовики» делали именно таким образом поддержку PAE (когда процессор в 32 битовом режиме мог адресовать >4Gb физической памяти) в процессах. Создавались shared memory по 256-512Mb в большом количестве и по-очередно мапились в процесс и вымапливались старые, когда нужен был доступ к этой памяти. А те 2Gb — это как раз то место куда ОС мапит такие вещи по дефолту, чтобы не мешать хипу: MapViewOfFile()/UnmapViewOfFile().

Также люди которые понимают как это работает — делали ballooning памяти через VirtualAlloc(...,MEM_RESERVE,...) — когда процесс при старте может разметить себе виртуальную память под свои нужды без выделения памяти, и затем мапить туда куски системной памяти для работы по нужным адресам. Для этого никаких опций компилятору давать не нужно было — это работает из коробки, если выйти за пределы стандартной библиотеки.

techcommunity.microsoft.com/...​ysical-memory/ba-p/723674 — там для сервера вінди цікаво розповідає чувак (шле привіт нам з далекого 2008).
Звичайно, що можна написати свій менеджер пам’яті, не такий перебірливий до ресурсів.
Я думаю що це задача на декілька днів для справжнього майстра!

вінди цікаво розповідає чувак

это ж Марк Русинович, его Windows Internals должна быть как библия для тех кто серьезно под винду пишет
Но по правде говоря тяжело у меня эта книга шла :)))
7я редакция покрывает современные версии винды
docs.microsoft.com/...​sources/windows-internals

Не 3 а 4 Гб. Учи матчасть.

Т.е. стека у тебя нет, и ты даже не смотрел с чем линкуется обычное консольное приложение — там в адресное пространство клиента мапируются мегабайты dll’ек, они все должны быть в непрерывном виртуальном адресном пространстве, поэтому ОС резервирует память под них и под будущие загрузки. Винда не знает будешь ли ты что-то динамически подгружать или нет, и если будешь память под них остаётся в резерве — хочешь ли ты этого или нет.

Я как понял ты уже разобрался в алгоритме и вынес вердикт ?

Зачем мне весь алгоритм? Если ты пишешь говнокод, который хранит поинтеры в uint32_t вместо uintptr_t, то я могу только тебе посочувствовать.

Т.е. стека у тебя нет

Стек в моем адрессном пространстве. Занимает аж 1 мб, если не изменяет память.

там в адресное пространство клиента мапируются мегабайты dll’ек,

Осталось разобраться сколько мегабайтов длл мапируется к HArray которые вызывает new да delete и иногда чтото на консоль выводит.

Зачем мне весь алгоритм?

Незачем тебе, здесь я согласен.

Нет там никаких ошибок в коде.

чувак, тобі не бракує хуцпи брехати, коли всі ходи записані і на 5 році виявляється, що структура не вміє хендлити неіснуючі ключі, тупо повертає 0
dou.ua/...​rums/topic/18849/#2134680

Нагрузочные тесты все прекрасно показывают. Были тесты (не я делал) миллиард ключей вставляли.

нагрузочні тести працюють в одній функції, там де init/insert/get/delete/destroy

як тільки розносиш їх в різні закутки і починаєш робити отаке
dou.ua/...​rums/topic/18849/#2135157

1) точно такий же рядок, але за іншою адресою — ключ буде інший?
2) переписати рядок за адресою str — ключ лишається той же?

так одразу видно де зачітив і що структура тупо по можливостям не здатна конкурувати з std::map, чи Python dict

Успокойся уже.

вот такі да :-)

одразу видно де зачітив і що структура тупо по можливостям не здатна конкурувати з std::map

Так, заявляти про неї як заміну мепам/хеш-таблицям це дещо нахабно. Але чисто теоретично може бути задача коли треба швидко ключ->ідентифікатор знаходити. І тоді за умови що клієнт:
— сам реалізує хеш-функцію
— пам’ятає усі ключі
— гарантує існування об’єктів раніше доданих у колекцію
— гарантує незмінність об’єктів раніше доданих у колекцію

все це може і буде літати. Але навіть для цього потрібна нормальна документація, нормальні тести та приклади, нормальні бенчмарки та щоб усе це просто збиралося різними компіляторами під різні платформи.

я правильно розумію, що ключі та значення в цій структурі можуть бути лише числами і лише користувач розбирається як їх інтерпретувати?

Саме так. Він реалізував доволі оптимальний спосіб збереження значень фіксованого розміру. В поточній реалізації це 32-розрядні значення наскільки я розібрався.

Нет, ключ может быть переменной длины. Для этого при вставке передается длина ключа. Велью фиксировано действительно 4 байтами, потому что чаще всего это поинтер на объект.
Но это можно переделать, просто в моих сценариях мне это не нужно.

Легендарный супероптимизированый алгоритм, который реализует весьма сложные Trie структуры данных и топчет вот эти вот эти ваши самые реализации ассоциативных массивов std::map, glib, dictionary.net и прочью заморскую попсу в разы, а то и на порядок.

а насправді

Велью фиксировано действительно 4 байтами, потому что чаще всего это поинтер на объект.
Но это можно переделать, просто в моих сценариях мне это не нужно.

Я написав

Він реалізував доволі оптимальний спосіб збереження значень фіксованого розміру

на що ти відповідаєш

Велью фиксировано действительно 4 байтами

Наче ж одне й те саме?

Так а че 4 а не 8? Ты уже в Azure и AWS не найдешь x32 виртуалок вообще, все x64, даже ARM.

тобто я можу використовувати як ключ рядки?

Да, ключ это любой массив байт любой длины выровненный по границе в 4 байта (в целях быстродействия)

чувак, тобі не бракує хуцпи брехати, коли всі ходи записані і на 5 році виявляється, що структура не вміє хендлити неіснуючі ключі, тупо повертає 0
dou.ua/...​rums/topic/18849/#2134680

*Фейспалм*
Какие будут следующий ошибки ?
Ошибки комментариев в коде ?

*Фейспалм*
Какие будут следующий ошибки ?
Ошибки комментариев в коде ?

ах, то для тебе це мєлочь? :-)
це у нас тут фейспалм 5 років тому і зараз знову
не диво написати тести, які вставляють і одразу читають задані значення із якогось масиву, а от на реальних задачках такі симульовані тести уже побоку
та блін, навіть простим читанням твого коду баги знаходяться, я мовчу вже про всілякі сегфолти, що були в людей раніше

На реальных задачах, по крайней мере те что у меня под рукой сейчас
std::map и hastable ___вообще___ не подходят по ____функциональности____

Остальное мне уже не интересно.
Баги иди в своем формошлепстве ищи.
Ты слишком т/глуп чтобы понимать как работают такие вещи. Иди нещасный LRU переучивай к собеседованию по 20 кругу (чего кстате меня и бомбануло. Люди до лру не могут додуматься, а некоторым рассказали и они забыли к следующему собесу).

На реальных задачах, по крайней мере те что у меня под рукой сейчас
std::map и hastable ___вообще___ не подходят по ____функциональности____
Остальное мне уже не интересно.
Баги иди в своем формошлепстве ищи.
Ты слишком т/глуп чтобы понимать как работают такие вещи. Иди нещасный LRU переучивай к собеседованию по 20 кругу (чего кстате меня и бомбануло. Люди до лру не могут додуматься, а некоторым рассказали и они забыли к следующему собесу).

хи-хи, гляди, як тебе заїло

тепер по пунктам
1. я хз, які у тебе реальні задачі, але твоя бібліотека обмежена і з багами
2. я не формошльоп, а ти хужеформошльопа — бо у тебе синдром Даннинга-Крюгера помножений на віндузятництво программінга
3. як бачиш, не глупий — розковиряв і потестив твій код (хоча я на C/C++ дуже давно не писаа), знайшов ще один баг — вот ти і злішся
4. на реалізацію LRU з віддебажженям і проходженням всіх тестів у перший раз у мене пішло 90 хв
5. запиляй LRU тут і розкажеш потім, скільки це зайняло часу — leetcode.com/...​ms/lru-cache/description . Має бути набагато швидше, ніж тут свій недороблений код захищати :-)

3. як бачиш, не глупий — розковиряв і потестив твій код (хоча я на C/C++ дуже давно не писаа), знайшов ще один баг — вот ти і злішся

Какой баг ? Это максимум тех депт, а минимум вкусовщина.

Уровень анализа у вас царапина на сопле истребителя. Это тех депт максимум.
Возвращать 0 вместо поинтера на объект если объект не найден.

Уровень анализа у вас царапина на сопле истребителя. Это тех депт максимум.
Возвращать 0 вместо принтера на объект если объект не найден. Не позорься

HArrayInt повертає uint32, 0, але не поінтер
ти читати мої комменти і свій код не вмієш
dou.ua/...​rums/topic/18849/#2134680

не позорся

HArrayInt повертає uint32, 0, але
не поінтер

Ты не можешь конвертнуть 32 бита в 32 бита в 32х битном процессе ?
(uint*)value

Печаль.

Ты не можешь конвертнуть 32 бита в 32 бита в 32х битном процессе ?
(uint*)value
Печаль.

1. Примітивно працюєш і намагаєшся увільнути.
2. Свого коду не знаєш.
Ти тестуєш запихання int‘ів, а не pointers
github.com/...​aster/HArray/Main.cpp#L87
3. у теперішній реалізації неможливо відрізнити, чи прочитався 0 по ключу, чи цього ключа немає. Бо «вкусовщина» супералгоритму.

3. у теперішній реалізації неможливо відрізнити, чи прочитався 0 по ключу, чи цього ключа немає. Бо «вкусовщина» супералгоритму.

Если для тебя критичен этот 0, используй следующую функцию.

github.com/...​ray/HArray_hasPartKey.cpp

Она проверяет есть ли ключ в контейнере.
Возвращает true/false. В отличии от бинарных деревьев и хештаблиц, может чекать ключи не только на есть ключ/нет ключа, но и есть ли вообще любой ключ который подходит под заданный шаблон. Функция работает за константное время, где константа это длина переданного шаблона О(1).

PS: Не пиши мне больше. Я вижу что ты решил взять меня измором своей тупостью.

Если для тебя критичен этот 0, используй следующую функцию.
github.com/...​ray/HArray_hasPartKey.cpp
Она проверяет есть ли ключ в контейнере.
Возвращает true/false. В отличии от бинарных деревьев и хештаблиц, может чекать ключи не только на есть ключ/нет ключа, но и есть ли вообще любой ключ который подходит под заданный шаблон.

посібо

Тепер перепиши testHArrayInt, щоб перед кожним getValueByKey воно дьоргало hasPartKey.
Запусти тести. Що буде — ой, уже не таке швидке, як раніше.

PS: Не пиши мне больше. Я вижу что ты решил взять меня измором своей тупостью.

я тільки почав, не треба твою хуцпу вважати моєю тупістю

тепер по ось цьому питанню дай відповідь
dou.ua/...​rums/topic/18849/#2135372

він там відповів вже (побіг за попкорном)

Тепер перепиши testHArrayInt, щоб перед кожним getValueByKey воно дьоргало hasPartKey.

Если бы я был говнокодером вроде тебя, то наверное так бы и написал. Но я же не говнокодер.
Поэтому getValueByKey переписываю специально для тебя за 5 минут, так чтобы абсолютно не потерять в скорости.

uint32 getValueByKey(uint32 key, bool& isExists)
	{
                isExists = true;

		HeaderCellInt& headerCell = pHeader[key >> BLOCK_BITS];
		uint32 rightPart = (key & BLOCK_SIZE);

		switch (headerCell.Type)
		{
		case 1:
		{
			if (headerCell.Code == rightPart)
			{
				return headerCell.Offset;
			}

			break;
		}
		case 2:
		{
			DoublePageInt* pPage = pDoublePages[headerCell.Offset >> 16];
			if (pPage)
			{
				DoubleValueCellInt& valueCell = pPage->pValues[headerCell.Offset & 0xFFFF];

				if (headerCell.Code == rightPart)
				{
					return valueCell.Value1;
				}
				else if (valueCell.Code2 == rightPart)
				{
					return valueCell.Value2;
				}
			}

			break;
		}
		case 3:
		{
			uint32 offset = headerCell.Offset + rightPart;

			MultiplyPageInt* pPage = pMultiplyPages[offset >> 16];
			if (pPage)
			{
				MultiplyValueCellInt& valueCell = pPage->pValues[offset & 0xFFFF];

				if (valueCell.Code == headerCell.Code)
				{
					return valueCell.Value;
				}
			}

			break;
		}
		};
               
                isExists = false;
		return 0;
	}
Возвращает флаг isExists найдено\не найдено.
Не благодари.
Если бы я был говнокодером вроде тебя

чувак, май на стримання, тут твій засраний код на GitHub висить, я ж там ковиряюся, бо у мене на цьому тижні приступ доброти і агрономії, аналіз як це на добрива перетворити

Возвращает флаг isExists найдено\не найдено.
Не благодари.

не буду, але хотів

тепер додай перевірку на isExists в тести
стало швидше? :-)

і т.д., ця низькорівнева реалізація без допилювання напильником і обв’язки перевірками купи фунцій, обв’язками alloc/destroy memory і т.п. — може бути швидкою і коректною тільки у вакуумі бенчмарків

в ріл-лайф її пустити — себе не поважати

Мне уже перестает быть интересным этот топик на доу.
За 5 лет существования этой темы, я помню единственный баг (не косметика) нашел канадский архитектор контрактор на ixbt. Я пофиксил этот баг за минут 15, но это был реальный баг в имплементации алгоритма, провтыкал одно место.

Не вижу смысла продолжать общение с всякими пинками флойдами, которые мне выносят мозг даже не за косметику, а так, за вкусовщину. Но гонору у них ...

Начни писать юнит тесты. Пока их нет, твой проект работает по happy-path. С тестами сам найдешь несколько десятков багов :)

Начни писать юнит тесты. Пока их нет, твой проект работает по happy-path. С тестами сам найдешь несколько десятков багов :)

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

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

Легко пропустить, если ворочаешь десятками миллионов, но не дошел до 70 миллионов, как я ниже продемонстрировал :)

. Каждая строчка в коде покрыта тысячи если не сотни тысяч раз в разных комбинация.

Як ти збирав статистику покриття, яким кодом?

За 5 лет существования этой темы, я помню единственный баг

dou.ua/...​rums/topic/18849/#2044301

Там у тебя еще вроде с сегфолтом падает при 70 миллионах ключей, уже пофиксил?

Мне уже перестает быть интересным этот топик на доу.

канєшно, якщо за всяку муру і недоробки, провтики, проскакування гострих кутів і тд тебе постійно макають в твоє творіння — то «художніка каждий может обідєть» :-)
але хуцпи при тому...

Какой баг ? Это максимум тех депт, а минимум вкусовщина.

:-D, блін, я з тої хуцпи не можу

5. запиляй LRU тут і розкажеш потім, скільки це зайняло часу — leetcode.com/...​ms/lru-cache/description . Має бути набагато швидше, ніж тут свій недороблений код захищати :-)

Когда уже дойдет что НЕ ИНТЕРЕСНО имплементить простые задачи, что уже десятки тысяч раз заимплеменчены. Тем более в академических целях.
Самый высший пилотаж заимплементить фундаментально иначе то, что никто и никогда не имплементил до тебя. И получить качественно другой результат.
Вот высший пилотаж.

Когда уже дойдет что НЕ ИНТЕРЕСНО имплементить простые задачи, что уже десятки тысяч раз заимплеменчены. Тем более в академических целях.
Самый высший пилотаж заимплементить фундаментально иначе то, что никто и никогда не имплементил до тебя. И получить качественно другой результат.
Вот высший пилотаж.

ти рядок покажеш, чи будеш пілотірувати далі високо словоблудієм?
dou.ua/...​rums/topic/18849/#2135372

НЕ ИНТЕРЕСНО имплементить простые задачи

Вибач, але починати доведеться з простих. До того ж ти сам заявив про три різні реалізації LRU які зробив за 15 хвилин. Мені особисто цікаво подивитися на них.

а це що таке?
github.com/...​ray/HArray_insert.cpp#L23
mapping string <=>uint32?
а чого uint32*, а не char*?

uint32 HArray::insert(uint32* key,
					uint32 keyLen,
					uint32 value)

Причому ключ фіксований до 64 байт?
github.com/...​ster/HArray/Main.cpp#L831

const char STR_KEY_LEN = 64;

А як на рахунок string <=> string?

const char STR_KEY_LEN = 64;

Это в бенчмарке задается на строках какой длины будем тестировать.
В данном случае берутся рандомные строки в 64 символа.

А як на рахунок string <=> string?

Не совсем понял ?
char* str = «hello world»;
uint32 value = (uint32)str;

Для 32х битного процесса (даже на 64х разрядной системе) должно сработать.

Не совсем понял ?
char* str = «hello world»;
uint32 value = (uint32)str;

Для 32х битного процесса (даже на 64х разрядной системе) должно сработать.

вот я от тоже не поняв, я думав, що там можна string<=>string,
але там нікуди контент value не копіюється, тобто я очікував, що всередині insert буде strcpy, чи memcpy, а там тільки таке
github.com/...​ay/HArray_insert.cpp#L114

pContentPage->pContent[contentIndex] = value;
char* str = «hello world»;
uint32 value = (uint32)str;
Для 32х битного процесса (даже на 64х разрядной системе) должно сработать.

Facepalm. Не будет оно работать на 64х разрядной системе.

char* str = «hello world»;
uintptr_t value = (uintptr_t)str;

Будет, поскольку в 64х разрядной системе можно запустить 32х битный процесс
(поинтер = 4 байта будет в контексте процесса)

Будет, поскольку в 64х разрядной системе можно запустить 32х битный процесс
(поинтер = 4 байта будет в контексте процесса)

Чекай. То ти стверджуєш, що в HArray можна зберігати strings, як values, якщо покласти в value поінтер на на string?

Будет, поскольку в 64х разрядной системе можно запустить 32х битный процесс

Мова про те, щоб зробити 64-бітний процес.

uint32 value = (uint32)str;

Конгеніально! Що буде коли:
1) точно такий же рядок, але за іншою адресою — ключ буде інший?
2) переписати рядок за адресою str — ключ лишається той же?

А взагалі то це дуже хитро примусити код клієнта перейматися копіюванням, часом життя та консистентністю даних в такій колекції, а самому просто числа тасувати. Я не кажу, що воно не потрібно, але щоб це стало заміною звичайним map та set треба дописату купу коду.

Одному коду клиента надо хранить велью Инты. Другому строки. Третьему (что чаще всего) объекты. Я решил что велью это поинтер. Могу собрать реализацию где велью будет 8-16 или сколько надо байт и встраиваться в HArray. Хотя не вижу в этом смысла, в листьях обычно лежат объекты, часто разного размера. Но всё-таки если кому надо, могу за деньги собрать такую реализацию

Я решил что велью это поинтер

Я зрозумів. І це нормальний підхід, просто мені з документації не очевидно було, що часом життя значень та їх іммутабельністю має перейматися клієнт.

Одному коду клиента надо хранить велью Инты. Другому строки. Третьему (что чаще всего) объекты. Я решил что велью это поинтер. Могу собрать реализацию где велью будет 8-16 или сколько надо байт и встраиваться в HArray. Хотя не вижу в этом смысла, в листьях обычно лежат объекты, часто разного размера. Но всё-таки если кому надо, могу за деньги собрать такую реализацию

вот
коли у тебе Value буде «сколько надо байт и встраиваться в HArray» — тоді будуть зовсім інакші тести з stdmap і вимоги до пам‘яті, плюс танці з аллокацією/деаллокацією.

Какая аллокация деалокация. Когда же вы научитесь код читать. Все велью хранятся в контент пейджах. Сейчас это 4 байт. Ставишь больше и цикл форич пишет велью такой длины которой надо в контент пейдж. Я ниодин new не добавлю в имплементацию.

На счёт std::map он тестировался с велью 4 байта. Все честно.

Какая аллокация деалокация.

ніяка, я ж написав «будуть»

Когда же вы научитесь код читать. Все велью хранятся в контент пейджах. Сейчас это 4 байт. Ставишь больше и цикл форич пишет велью такой длины которой надо в контент пейдж.

я код читав, такого не бачив, не дивно не побачити, бо функція на 1300 чи скільки там рядочків — це звісно з Best Practices

покаж пальцем в рядок на Github де цей цикл?

Там где велью переданное в insert копируется. Твой Кеп.

Там где велью переданное в insert копируется. Твой Кеп.

пальцем ткни, я лінки наводив
не полінись і ти

А вот принципиально не буду. Если человек не может найти в коде где копируется переменная value в одной единственной функции, то это о чем-то говорит

Вообще я вспомнил, я для тебя мультик снял. Есть на центральной странице проекта на гите. Визуально сможешь зафиксировать что никаких ограничений на размер велью нет ?

Вообще я вспомнил, я для тебя мультик снял. Есть на центральной странице проекта на гите. Визуально сможешь зафиксировать что никаких ограничений на размер велью нет ?

блін, ну це капєц якийсь
тут Artyom постійно дістає тебе з «show me your code», у тебе вже і код є — а ти мультікі показуєш

Вообще я вспомнил, я для тебя мультик снял

А треба було лінк на рядок коду дати усього лише.

А вот принципиально не буду. Если человек не может найти в коде где копируется переменная value в одной единственной функции, то это о чем-то говорит

бо її нема і ніякого копіювання в форіч нема
те що я бачив ще вчора — це присвоєння value (uint32), а ти розповідаєш про якийсь цикл і «такой длины»

покаж рядок — я вибачуся

покаж рядок — я вибачуся

Ок. Вот цикл который пишет хвост ключа и в конце записывает Value. Если велью больше чем 4 байта, то цикл продолжается и мы записываем столько сегментов Value сколько нужно (это модификация о которой я говорил).

github.com/...​y/HArray_insert.cpp#L1533

Жду вибачень.

Ок. Вот цикл который пишет хвост ключа и в конце записывает Value. Если велью больше чем 4 байта, то цикл продолжается и мы записываем столько сегментов Value сколько нужно (это модификация о которой я говорил).

Посібо, я знав, що це поруч
Покажи модифікацію, яка буде робити наступне «Ставишь больше и цикл форич пишет велью такой длины которой надо в контент пейдж.»

Поки що я там бачу (і бачив раніше, за що і почав розпитуватися) тільки присвоєння value (4 байти)
github.com/...​y/HArray_insert.cpp#L1539

Жду вибачень

я б хотів вибачитися, але ти розказуєш про неіснуючі штуки
покажи ось це
«Ставишь больше и цикл форич пишет велью такой длины которой надо в контент пейдж.»

Погоди. Я свою часть договора выполнил.

я б хотів вибачитися
Жду вибачень.

Про то что я буду это имплементить я не сказал. Я обещал что нет _никакой_ проблемы заимплементить модификацию которая будет сохранять велью больше чем 4 байта и показал где это можно сделать. И сделаю это за деньги если кому надо, это тоже я говорил.

Так что жду вибачень.

2Punk Floyd

Так, у тебя еще 15 минут. Дальше у меня дежурная прогулка по лесу и набережной озера, наблюдая закат с мыслями о суперсовершенных алгоритмах.
Меня устроит вполне скромная фраза без излишиств
"Вибач мене будьласка я був неправий"©

бо її нема і ніякого копіювання в форіч нема
те що я бачив ще вчора — це присвоєння value (uint32), а ти розповідаєш про якийсь цикл і «такой длины»
покаж рядок — я вибачуся

«бо її нема» — а нема, то й нема за що вибачатися

не треба було телитися цілу добу, щоб потім підтвердити очевидне

і вертаємся до попереднього

dou.ua/...​rums/topic/18849/#2135223
«вот
коли у тебе Value буде „сколько надо байт и встраиваться в HArray“ — тоді будуть зовсім інакші тести з stdmap і вимоги до пам‘яті, плюс танці з аллокацією/деаллокацією.»

«бо її нема» — а нема, то й нема за що вибачатися

не треба було телитися цілу добу, щоб потім підтвердити очевидне

Тоесть ты забалаболил и свою часть уговора не выполнил. Ок.

і вертаємся до попереднього

dou.ua/...​rums/topic/18849/#2135223
«вот
коли у тебе Value буде „сколько надо байт и встраиваться в HArray“ — тоді будуть зовсім інакші тести з stdmap і вимоги до пам‘яті, плюс танці з аллокацією/деаллокацією.»

Ну почему ты такой тугой. Ты всего лишь пишешь велью на страницу. Если велью 4 байта то и я и стд МАП тратит на это условных 4 такта. Если мы будем писать 8 байт то и я и мапа будет тратить только условных 8 тактов
Это блеать велью, все время что ты тратишь просто тратишь на запись последовательно байт. Ну неужели нельзя это понять.

Тоесть ты забалаболил и свою часть уговора не выполнил. Ок.

балабол ти, бо ти мені показав те, чого не існує, і про я писав раніше, що не існує, претензії тільки до тебе самого

Ну почему ты такой тугой. Ты всего лишь пишешь велью на страницу. Если велью 4 байта то и я и стд МАП тратит на это условных 4 такта. Если мы будем писать 8 байт то и я и мапа будет тратить только условных 8 тактов
Это блеать велью, все время что ты тратишь просто запись последовательно байт. Ну неужели нельзя догадаться ??

Тугий у нас тут ти. А може просто чітер.
Порівнювати і ганяти тести зберігання у твоїй структурі 1 (одного!) поінтера на структуру проти std::map і подібних структур, де value (variable length) копіюється всередину.

Ще раз
«вот
коли у тебе Value буде „сколько надо байт и встраиваться в HArray“ — тоді будуть зовсім інакші тести з stdmap і вимоги до пам‘яті, плюс танці з аллокацією/деаллокацією.»

std::map

Жесть. Покажи мне что в мапе копируется кроме велью = 4 байта в моих бенчмарках.
Ты реально сейчас в топе среди всех тупых отписавших в этом топике примерно за последний год. Первенство держит все ещё хлопец который предложил переписать на ООП.
Но ты уже близок.

Жесть. Покажи мне что в мапе копируется кроме велью = 4 байта в моих бенчмарках.

звиняй, у кишках std::map не ковирявся, але припускаю, що там буде
1) alloc пам’яті, якщо value менше, ніж уже pre-alloc
2) копіювання ВСЬОГО value туди. Value може бути variable length і для цього у std::map є туча коду

У твоїх так званих benchmarks ти використовуєш std::map не на повну потужність, записуєш туди огризок і пишаєшся тим.
Але як тільки ми вийдемо за межі огризка, і почнем зберігати у твоїй структурі variable length дані, так одразу буде видно хто і де насрав, і чому std::map та й інші структури/бібліотеки мають купу оверхеду.

Ты реально сейчас в топе среди всех тупых отписавших в этом топике примерно за последний год. Первенство держит все ещё хлопец который предложил переписать на ООП.
Но ты уже близок.

Ти на жаль припускаєшся дурної помилки через свою хуцпу — вважаєш себе геніальним, а всіх решта по дефолту тупими. Якщо ти це перестанеш робити (а це неможливо, бо хуцпа) — то ти зможеш вчитатися і зрозуміти що тобі пишуть, без переходу одразу в атаку.

1) alloc пам’яті, якщо value менше, ніж уже pre-alloc
2) копіювання ВСЬОГО value туди. Value може бути variable length і для цього у std::map є туча коду

Можна передавати володіння елементами, але це все одно вимагає чогось більшого ніж набір 4-байтних значень.

что в мапе копируется кроме велью = 4 байта в моих бенчмарках

Мапа, на відміну від твоєї структури робить копії об’єктів, а також вміє переміщувати їх та передавати володіння. У тебе ж просто 4 байти фіксовано. До того ж у мапи є такі чудові речі як ітератори, можливість використовувати її як абстрактний контейнер з алгоритмами, можливість прикрутити алокатор і тому подібне. І все це «з коробки» і без того щоб автори мапи «от щас спеціально для тебе за 5 хвилин переписав як треба».

Для того щоб серйозно заявляти про хоч якусь наближеність твоєї структури до тієї ж мапи тобі мінімально треба:
— зробити з неї ADT колекцію
— додати хеш-функції
— додати ітератори
— покрити все юніт-тестами
З цим можна буде говорити вже про бенчмарки.

А для використання в реальних проектах треба ще:
— константні ітератори
— алокатори
— діапазони
— ...

Мапа, на відміну від твоєї структури робить копії об’єктів, а також вміє переміщувати їх та передавати володіння.

Копии каких обьектов она делает ? Любой обьект класса в Си кроме структур, это указатель. Сделав указатель я покрыл почти 100% случаев. Какие вопросы ?

Для того щоб серйозно заявляти про хоч якусь наближеність твоєї структури до тієї ж мапи тобі мінімально треба:
— зробити з неї ADT колекцію

Моя структура и так абстрактная. Ей всеравно что такое ключ и что такое велью. Это набор битов.

— додати хеш-функції

Мне это не нужно. Плюс моей структуры, что она работает без хешфункций и сортирует данные.

— додати ітератори

Там есть визитор.

— покрити все юніт-тестами

Бенчмарки теже тесты, все покрывают. Проблем нет.

З цим можна буде говорити вже про бенчмарки.

Можно. Но для начала мапе нужно научится следующее.
1. Научится компрессить данные для ключей с одинаковым префиксом — она это не умеет
2. Научится сканировать поддиапазоны ключей, например все ключи которые начинаются с такогото префикса — она этого не умеет
3. Реализовать функцию hasPartKey которая работает как поиск ключа по шаблону за О(1) — не умеет
4. Научится удалять ключи, с плавным освобождением памяти, балансировкой и шринком страниц — не умеет
5. Научится вставлять и искать ключи почти за константное время О(1) — не умеет
6. Сохранятся на диск на уровне скорости работы дисковой системы — не умеет
7. Искать по диапазону ключей за тоже близкое к О(1) время — не умеет.

Я этот список могу продолжать долго.
Вы просто не понимаете рассовое превосходство моего алгоритма. Это просто другой уровень, чем гавеные деревья и хештаблицы. И прежде всего даже не по скорости, а по функциональности.

Копии каких обьектов она делает ? Любой обьект класса в Си кроме структур, это указатель. Сделав указатель я покрыл почти 100% случаев. Какие вопросы ?

блд, на що я трачу свій час....

С бубуном общаться — или постебаться с него или послать нах. Второе на форуме типа запрещено :-)

Копии каких обьектов она делает ?

Будь-яких, що збергіаються в ній.

Любой обьект класса в Си кроме структур, это указатель.

Вказівник — це вказівник, а об’єкт — це об’єкт.

Сделав указатель я покрыл почти 100% случаев

Ти покрив лише випадок коли треба зберігати вказівники. А це насправді не так часто трапляється. Як в твоїй колекції зберігати той же std::string? І так, мені треба зберігати саме в колекції як я це можу зробити з std::map, а не перейматися часом життя тих рядків.

Моя структура и так абстрактная.

Це не правда. Як в ній зберігати std::string?

Мне это не нужно.

Це потрібно користувачу твоєї колекції. Інакше для кожного типу даних мені доведеться писати хеш-функцію.

Там есть визитор

Тобто зробити так, щоб твою структуру можна було використовувати з алгоритмами має бути не складно.

Бенчмарки теже тесты, все покрывают

Покажи статистику покриття.

Но для начала мапе нужно научится следующее

Мапі нічого не треба. Те що її без проблем використовують в безлічі проектів на протязі пари десятків років це лише підтверджує. Це тобі свою реалізацію треба підтягнути до тієї ж мапи, якщо хочеш з нею порівнювати.

Вы просто не понимаете рассовое превосходство моего алгоритма

Ти визначся чи в тебе алгоритм, чи структура даних. Якщо таки алгоритм то чому його не запиляти так, щоб можна була використовувати в тих же stl колекціях?

Это просто другой уровень, чем гавеные деревья и хештаблицы.

І поки що це рівень proof of concept.

Завели оффтопік в іншому топіку — dou.ua/...​rums/topic/33483/#2134487
Тепер вертаємся сюди

Що робити, коли не знайдений елемент у hashtable?

1. Реалізація C++ — std::map — find en.cppreference.com/w/cpp/container/map/find

Return value

Iterator to an element with key equivalent to key. If no such element is found, past-the-end (see end()) iterator is returned.

2. Реалізація hashtable у класичній книжці по C — Кернігана і Річі
github.com/...​chapter06/tablookup.c#L72
Структура і повернення

struct nlist *np;
...
return NULL; // Not found

3. Реалізація Бубна
github.com/...​r/HArray/HArrayInt.h#L484

uint32 getValueByKey(uint32 key)
...
return 0;

Швидкий тест, звиняйте, що без засікання часу :-D

void testHArrayZeroInt()
{
        printf("HArrayInt => ");

        HArrayInt ha;
        ha.init(26);

        for (unsigned int i = 0; i < 10; i++)
        {
                printf("got value %u from key %u\n", ha.getValueByKey(i), i);
        }

        ha.destroy();
}

Вивід

$ ./HArray
HArrayInt => got value 0 from key 0
got value 0 from key 1
got value 0 from key 2
got value 0 from key 3
got value 0 from key 4
got value 0 from key 5
got value 0 from key 6
got value 0 from key 7
got value 0 from key 8
got value 0 from key 9

Я поменяю. Но NULL в Си насколько помню это и есть int* p=0. Тоесть поинтер вникуда.

Я поменяю. Но NULL в Си насколько помню это и есть int* p=0. Тоесть поинтер вникуда.

ти не можеш з C функції, що вертає цілочисельне значення — вернути NULL, який буде інтерпретований як 0, який цілком може бути валідним значенням

функція або має вертати структуру, яку ще потім треба обробити, або писати значення в кудась по поінтеру і вертати true/false (якщо не знайдений і не записаний елемент)

і вертати true/false (якщо не знайдений і не записаний елемент)

Можно и так сделать. Там это без разницы.

NULL в Си насколько помню это и есть int* p=0

Не гарантовано. NULL можу бути і не 0, але (void*)0 завжди NULL. Також про nullprt в плюсах глянь.

Например на LynxOS NULL — это валидный поинтер, также на разных PPC Cisco продуктах NULL тоже валидный.

Я зараз не згадаю де точно, але колись на початку кар’єри працював з embedded програмістами які для крихітних проців з пару кіло пам’яті писали і у них там для однієї платформи NULL був щось типу 07777.

Перша сторінка пам’яті — також місцями традиційно NULL... тобто GPF...

Когда это будет в boost ?

Когда они это купят и будут платить per CPU core

Добавил анимацию как эта штука работает.
Схема правда упрощенная, все ньюансы не вместить в гиф.
raw.githubusercontent.com/...​Array_works_animation.gif

там все настолько быстро переключается, что я не успеваю понять

все равно без доки нифига не понятно. надо в поверпойнт по слайдам с текстом.

Все верно: когда быстро — завораживает, но для понимания — хреново. Особенно если знание очень сильно изолированное. В этом случае надо всю предметную область step by step минута за минутой в мозг записывать.

Слайды мега круто будет сделать. И слайды рулят безусловно. И два кнопки — вперед и назад.
Хитрость таких кнопок — что всегда можно прийти к началу и всегда можно прийти к концу. И что можно менять стрелу времени с прямой на обратную. И что можно думать сколько угодно времени и разбираться с конкретным фреймом пока не разберешься, а потом переходить на следующий фрейм. И вообще это крутой бизнес — слайдирование (но об этом только спонсорам расскажу)
P.S. если не зацикливать, не замыкать круг, как вот сейчас, когда начало от конца ничем не отличается.

Читал сейчас твою статью на Хабре — захватывающе. И пришла мысль про «забывание» — редко затребываемые данные стоит забывать. Время доступа к ним сравнимо с временем обновления из источника. Что подводит ко взгляду на информацию, как на то, что имеет массу, плотность. x=2 это генератор двойки в икс. Но если потом y=x-2 то в икс не должно ничего остаться. Информация как песчинки, переносится от одного data store к другому, как песочные часы работают.
Это радует потому, что актуальность информации всегда будет на высоте там, где старая информация тускнеет, де-определяется, квантуется и исчезает.

Когда-как.
У крупных контор с миллионами клиентов небольшое ускорение кода может сэкономить очень много денег. Таким бывает не жалко посадить пару программистов оптимизировать код.
А еще бывают случаи, когда медленно — вообще не вариант. Например, для передачи звука в телефонии или для боев по сети в игрушках.

Проще не значит медленно. Напротив, проще всегда значит быстрее. Почему так? Потому что простота это всегда минимальная обработка сырого сырья некоторым набором правил. Чем больше правил применяется и или чем больше делается последовательных применений одного и того же правила т.е. вот так: a= f(a); a=f(a); a=f(a); //здесь три инструкции 
тем сложнее и медленнее работает код.
Отсюда следствие: если код сложный и медленный, то кто-то не нашел простого решения.
Например, как споласкивая чашку после чая, максимально быстро не оставить в чашке ни одной чаинки? Я нашел решение.

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

Поэтому когда он его упростит — этот алгоритм станет еще быстрее.

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

Например, нужно скопировать фрагмент памяти. Самое простое решение for (int i=0; i<len; ++i) *dst++ = *src++;, но копирование по одному символу достаточно медленное. Быстрее будет код, который копирует сразу по uint64_t или с использованием каких-то больших XMM регистров, но это приводит у сложнению кода: нам надо определить, сколько полных значений будет скопировано, а потом в конце докопировать невписавшийся остаток. Код сложнее, но быстрее.

Например, нужно скопировать фрагмент памяти. Самое простое решение for (int i=0; i < len; ++i) *dst++ = *src++;

С каких пор это стало проще, чем обычный memmove/memcpy?

но копирование по одному символу достаточно медленное. Быстрее будет код, который копирует сразу по uint64_t или с использованием каких-то больших XMM регистров, но это приводит у сложнению кода: нам надо определить, сколько полных значений будет скопировано, а потом в конце докопировать невписавшийся остаток. Код сложнее, но быстрее.

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

Дабы не быть голословным:
godbolt.org/z/4dEzeqjx7

С каких пор это стало проще, чем обычный memmove/memcpy?

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

Или вы считаете, что все задачи уже решены, и надо просто играться в лего?

1. Объясните, почему вы использовали префиксный инкремент (в скобках справа) вместо более логичного постфиксного?
2. Зачем копировать, если нужно переписывать с затиранием. Копировать — грешно. Информация материальна. Использование копирования нарушает закон сохранения массы-энергии. Копирование это как рак. Громоздкое и ригидное нечто порождает.
3.

Ну а дальше чем больше таких случаев, тем сложнее код.

Именно так. Поэтому соревнование, «а давайте сделаем проще код» — это очень правильное соревнование.
Допустим на входе у нас разношерстная куча данных. Ну и пусть тогда каждая утилита (простая) только свой тип данных и отбирает и обрабатывает.
Поддержка универсального комбайна или поддержка гаечного ключа, собирающего различного рода комбайны и тракторы и планеры и даже наверное космические корабли... Что выберете?

1. Более логично очень условно, но если брать C++, то там есть разные итераторы, для которых инкремент переопределён. Переопределённый постфиксный ++ должен вернуть старый объект, и оптимизатор не всегда мог понять, что эти действия не нужны.

2. — 3. Вечный флейм.

Вот еще вариант ответа (из жизни): Купюры в миллион долларов используют редко поэтому: ru.wikipedia.org/wiki/Свободные_деньги
надо нещадно их демерджить)))
А вот «трудовые» рублики — 10-20-50-100 хай сохраняются подольше.
Вот такое прогрессивное налогообложение. И заметь, это лучшее предложение из всех, которые нам современная инфоматика предлагает.

Вы же огласите алгоритм коллега ? Потребление памяти интересует

Потребление памяти интересует

Потребление памяти растет почти линейно, без скачков. Оверхед, примерно как у хештаблиц x2-x3, это Worst Case (когда ключи короткие и они рандом). Есть и Best Case для HArray, если ключи часто совпадают по префиксу (урлы, пути, длинные слова и тд) то контейнер может работать как архиватор при этом insert, lookup будет еще быстрее.

В следующем примере каждый ключ 64 байта. В первом случае 64х байтные ключи похожи между собой, отличаются несколькими байтами. Во втором случае 64х байтные ключи рандом.
Результаты такие.
Оригинальный датасет => 5 млн * (64 байта ключ + 4 байта вэлью) = 340 mb

=== HArray VS google::dense_hash_map VS std::map VS std::ordinary_map testing ===
Insert/Search/Delete 5000000 SIMILAR keys (64 bytes each) ...
HArray => Insert: 1552 msec, Search: 1694 msec, Delete: 6089 msec, Memory: 413 mb.
std::map => Insert: 8809 msec, Search: 8908 msec.
std::unordered_map => Insert: 3694 msec, Search: 1188 msec.

Insert/Search/Delete 5000000 RANDOM keys (64 bytes each) ...
HArray => Insert: 1883 msec, Search: 1872 msec, Delete: 8258 msec, Memory: 749 mb.
std::map => Insert: 7774 msec, Search: 7583 msec.
std::unordered_map => Insert: 3648 msec, Search: 1197 msec.

Вот кстате кейс. Увеличили размер ключа с 64х байт до 128ми байт.
Итого
Оригинальный датасет => 5 млн * (128 байта ключ + 4 байта вэлью) = 660 mb

Insert/Search/Delete 5000000 SIMILAR keys (128 bytes each) ...
HArray => Insert: 1824 msec, Search: 1899 msec, Delete: 6619 msec, Memory: 413 mb.
std::map => Insert: 12319 msec, Search: 12531 msec.
std::unordered_map => Insert: 5222 msec, Search: 1782 msec.

Получили сжатие ключей на 30% от оригинального размера. Такой себе архиватор.
При этом в этом же кейсе, стандартная хештаблица будет потреблять памяти примерно 1-2 гб.
Тоесть в разы больше чем это делает HArray.

1) С Джуди скорость сравнивали? Если у Вас быстрее — можно писать статью в Overload, и на гите появятся последователи.
2) Как оно себя ведет, когда ключи разной длины? Например, разбить Гутенберга на предложения, и пнуть туда.

1) С Джуди скорость сравнивали? Если у Вас быстрее — можно писать статью в Overload, и на гите появятся последователи.

dou.ua/...​rums/topic/18849/#1006289
Но это вроде был HArray для интов. Для ключей переменной длины нужно мерять.

2) Как оно себя ведет, когда ключи разной длины? Например, разбить Гутенберга на предложения, и пнуть туда.

Шунтирует ветки и вставляет ключи разной длины в один и тотже контейнер.
Единственое все они выровнены по границе в 4 байта (сегмент).
Тоесть длины ключей могут быть 4, 8, 12, 16, 20 и тд байт.

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

Бизнес план:
1) Делаешь dual license типа этой www.pjsip.org/licensing.htm
2) Пишешь статью в Оверлоад или еще какой читаемый журнал со статьями в открытом доступе
3) Пихаешь статью на сайты по хай-лоаду, например ithare.com
4) Имеешь клиентов
5) Когда клиент хочет больше и качественнее, просишь 20К
6) Отдаешь 10К Горчаку, он пихает свой префетч в проблемное место, и клиенту хорошо
7) Еще можно отдать 5К мне, я за две недели перепишу половину кода, и в нем потом вообще никто не разберется

Мой бизнесс план, пошатнуть на рынке Монгу.
Trie идеально подходит для упаковки и работы с джисонами.

Монга уже раскручена. Ее долго выбивать с рынка, можешь не успеть до пенсии. А пропихнуть структуру данных, которая разгрузит сервера на 30% — это выглядит реальнее.

В данном случае Монга, это пробный шар на рынке документоориентированных баз данных. Такой себе FoxPro или Access, без транзакций, без аспектов, с ограничением на размер документов, без приличной скорости, без аналитических запросов.
За которым идет Oracle, его время просто еще не настало.

Вот чувак описывает это как свою ошибку
dou.ua/...​ign=reply-comment#2048452

И зачем разработчикам переходить с монги...кстати куда переходить то?
Тут все пишут что код даже не читабельный :-)

Только не паникуй
ibb.co/Zh5Zkj4

PS: Это уже с ACID и транзакциями. Shapshot, Read commited, Repeatable read

кстати, видел, что sqlite самый быстрый?
sj14.gitlab.io/...​nch/databases_update1.svg

en.wikipedia.org/wiki/Write-ahead_logging
Вот статья из картинки sj14.gitlab.io/post/2018/12-22-dbbench
Насколько я понимаю, SQLite рвет всех потому, что он однопоточный на запись, и не нужны мютексы и другая хрень.

Ясно, WAL означает что все должно влезть в РАМ, что позволительно для SQLite, но не оч. позволительно для баз более серьезных.
По этой же схеме работает Тарантул. По этой же схеме и моя база, но я буду от этого потихоньку избавлятся или комбинировать разные методы.

WAL означает что все должно влезть в РАМ

нет

нет

Формально нет, по факту да. Под нагрузкой WAL будет писать быстрее, чем он будет успевать мержится в основной индекс. Поэтому красивые картинки с WAL обычно где все лежит в RAM, а на диск попадет как нибудь потом.

WAL тоже пишет на диск в журнал — для того, чтобы не потерялись данные если система упадет во время трансакции.

Да, но эти данные должни попасть в

успевать мержится в основной индекс

на диске. Журнал существует чтобы восстанавливать хвост последних операций на диске, но не всю базу.

Ну так если дойдет до этого — то нужно мерять в бенчмарках что там реально происходит. Вырубить процесс и глянуть, сколько записей он восстанавливает из журнала. Если там реально SQLite будет сравним с твоей базой.

Я имел в виду — померять перформанс не на синтетических данных, а на реальном гиге реальных предложений из книжек. Может отличаться.

То о чем ты говоришь, уже реализовано. BH использует HArray для построения инвертированного индекса.
github.com/Bazist/BH
Другими словами в HArray лежит словарь.
В случае Либрусека словарь на сотни гиг или 400 тыс книг..
Скорость индексации там достаточно большая. Около 60 мб\сек чистого текста.

Так а как сравнить с другими имплементациями?

Не знаю, нужно ли. Везде есть свои особенности. В данном случае при индексации HArray под нагрузкой что на синтетических данных, что на словарях держит около 10м\сек запросов на поиск\вставку. В один поток.
На удачных имплементациях хештаблиц, скорость будет похожей.
Может отличатся на 20-30%, но не существенно.

Чтобы писать статью и делать шум нужно показать воспроизводимый эксперимент на реальных данных, как ты уделываешь всех самых крутых.
И базу продать трудно — там и надежность, и репликация, и дофига всего. А вот подсадить конторы на свою таблицу, и доить за поддержку нечитаемого кода — почему бы нет.
А потом, когда будут деньги и шумок за таблицу — можно и базу допилить и продавать.

JudyArray слетел с рынка по причине, что люди не понимают зачем писать такие сложные структуры если есть хештаблицы. Хештаблицу можно дотюнить до скоростей Judy или HArray.
Но есть область, где бинарные деревья и хештаблицы вообще никак не подходят. И эта область в документоориентированных базах данных.
Такого еще никто не делал, нужно копать сильно глубже.

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

Оно то может и так. Но мы живем в неидеальном мире.
Даже вот по гугловской либе
github.com/...​blob/master/Benchmarks.md
dense_hash_map делает std::map как Бог черепаху.
Как ты думаешь, все бросились менять std::map на dense_hash_map ? Конечно нет. Интересен прикладной софт, а структура да хороша, но ее еще нужно правильно применить, где раскроются все ее лучшие стороны.

Может, dense_hash_map недостаточно рекламируют? Тем более, std::map это уже прошлое тысячелетие. В стандарте давно unordered_map.

Если влепить твою структуру в memcached и показать, что размер и скорость кеша увеличились на той же памяти, и поездить по короноконференциям — вполне можешь получить заказчиков, а потом на следующих выставках — хвастаться, что Нетфликс юзает твой софт.

Кстати, вот чувак писал свой кеш.
dou.ua/...​/?from=similar_posts_tech
Спроси, чем у него все закончилось. Может, расскажет про грабли, или историю успеха.

Это все интересно, но я тут подумал о распределенных базах знаний. Чем больше устройств хранят почти одну и ту же информацию, тем с большими шансами можно еще быстрее найти что-то необходимое данному пользователю. Тут тоже некая неопределенность квантовость в том, с какой машины этот пользователь сделает свой запрос.
Там где последовательно кто-то раз-два-три-четыре, распределенные штуки — раз-два, раз-два.

о распределенных базах знаний

Веб 3.0. Который хз когда еще придет.
www.e-xecutive.ru/wiki/index.php/Веб_3.0

Распределенные неопределенности чем-то похожи на материал, а когда они, квантово запутанные (например, утиной типизацией), встречаются с определителем то становятся материей. Веществом.
Мы скоро начнем создавать виртуальное вещество из нашего кода)))

Прорыв у вас? Потому что волновая функция у нас теперь совпадает по некоторым параметрам. Наверное. Одно дело делаем.

Олег, эта тема не выдержит двух поехавших :)

Ухожу, ухожу. Коммунальные надо заплатить, паспорт переоформить... Мои посты в помощь. Их даже можно просто как набор ключевых слов для активизации определенных областей нейронов использовать

Круто було б якби ти якийсь враппер зробив щоб твої коллекції можна було як SLT-коллекції юзати. Тоді можна було б написати простий тест для порівняння HArray з std::map, boost::hash_map та іншими. І хто завгодно міг би ці тести запустити будь-де за наявності С+±компілятора.

Бубен, я решил посмотреть как твой алгоритм сравнится со стандартной std::collections::HashMap

Делал только один бенчмарк — HArrayInt_VS_StdMap_IntKey, только с последовательными числами.

Код:
play.rust-lang.org/...​65150cc6fd17bcdbc7a352f46

Если будешь пытаться запустить на том веб-сайте Sandbox, переключи в режим Release, понизь кол-во итераций в 10 раз, и учти, что работать будет медленно, так как Sandbox работает на слабых виртуалках и не подходит для бенчмарка.

Бенчмарк запускал на Windows 10, Intel Core i9-9880H @2.3 GHz.

Rust в режиме release с включенной lto. Мой результат:

Keys count: 0, elapsed: 0
Keys count: 10000000, elapsed: 111
Keys count: 20000000, elapsed: 214
Keys count: 30000000, elapsed: 336
Keys count: 40000000, elapsed: 463
Keys count: 50000000, elapsed: 545
Keys count: 60000000, elapsed: 653
Keys count: 70000000, elapsed: 808
Keys count: 80000000, elapsed: 892
Keys count: 90000000, elapsed: 978
Keys count: 100000000, elapsed: 1085

В твоем коде я увеличил кол-во итераций, чтоб была равна моему, и закомментировал бенчмарки на сторонние хеш-таблицы:

	HArrayInt_VS_StdMap_IntKey(0,   //start
								10000000,   //step
								100000000); //stop
	for (uint32 countKeys = startOnAmount; countKeys <= stopOnAmount; countKeys += stepOfAmount)
	{
		printf("Insert/Search %u SEQUENCE keys (%u bytes each) ...\n", countKeys, sizeof(uint32));
		testHArrayInt(intKeys, countKeys);
		//testDenseHashMapInt(intKeys, countKeys);
		//testStdMapInt(intKeys, countKeys);
		//testStdUnorderedMapInt(intKeys, countKeys);
		printf("\n");
	}

Запускал твой код из-под Visual Studio в режиме Release/x64, без отладчика. Результат:

=== HArrayInt VS google::dense_hash_map<int,int> VS std::map<int,int> VS std::ordinary_map<int,int> testing===
Insert/Search 0 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 0 msec, Search: 0 msec.

Insert/Search 10000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 105 msec, Search: 32 msec.

Insert/Search 20000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 243 msec, Search: 90 msec.

Insert/Search 30000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 327 msec, Search: 110 msec.

Insert/Search 40000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 429 msec, Search: 131 msec.

Insert/Search 50000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 560 msec, Search: 158 msec.

Insert/Search 60000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 659 msec, Search: 192 msec.

Insert/Search 70000000 SEQUENCE keys (4 bytes each) ...
HArrayInt =>
C:\src\HArray\Release\HArray.exe (process 22280) exited with code -1073741819.
Press any key to close this window . . .

На 70 миллионов ключей твоя программа падает, почему не разбирался.

Сводная таблица

HArrayInt_VS_StdMap_IntKey / SEQUENCE_TESTS

keys count	    rust hashmap, ms	harrayint, ms
10000000        95                  105
20000000        192                 243
30000000        270	                327
40000000        374	                429
50000000        448	                560
60000000        572	                659
70000000        644	                error
80000000        726	                error
90000000        849	                error
100000000       957                 error

HArray сливает std::collections::HashMap по скорости insert/lookup 11-27% и вдобавок крашится на больших объемах данных.

Во-первых, сиквенц это ворст кейс для HArrayInt.
github.com/...​p_seq_32bits.png?raw=true
Во-вторых, ты сравниваешь структуры с сильно разной функциональностью. HArrayInt сортирует данные при вставки и предоставляет запросы типо вернуть ключи по ренжам, обойти сортированный список. Хештаблица ничего не сортирует и сохраняет как хеш пошлет.
В-третьих, Данные сильно синтетические. Возьми рандом. И если добавляешь 100 млн ключей, то возьми передай в конструторе сапасити как
HArrayInt ha;
ha.init(27);
или
ha.init(28);

и перемеряй.

ha.init(27);
или
ha.init(28);

Что эти магические цифры обозначают Почему 28 а не 100? Почему нет никакого нормального описания аргументов?

В ридми есть такое описание
HArray ha;
ha.init(); //ha.init(24) set your custom capacity for big datasets

Оно говорит каким будет размер хедера
2^24*4 байт

Я заменил

github.com/...​aster/HArray/Main.cpp#L77

	ha.init(26);

на

	ha.init(26);

теперь падает в самом начале, еще до того как я добавил какие-либо ключи

=== HArrayInt VS google::dense_hash_map<int,int> VS std::map<int,int> VS std::ordinary_map<int,int> testing===
Insert/Search 0 SEQUENCE keys (4 bytes each) ...
HArrayInt =>
C:\src\HArray\Release\HArray.exe (process 13500) exited with code -1073740791.
Press any key to close this window . . .

Бубен, библиотека очень косячная, как ты ее тестил?

ha.init(26);
на
ha.init(26);

Не совсем понял. Зачем 26 менять на 26 ?

Только что на моем ноуте iCore7, RAM 16gb
=== HArrayInt VS google::dense_hash_map VS std::map VS std::ordinary_map testing===
Insert/Search 70000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 1395 msec, Search: 297 msec.

std::map улетела с эрором. Наверно памяти не хватило.

Не совсем понял. Зачем 26 менять на 26 ?

Поменял на 28, опечатка была в сообщении. Твой код падает с ошибкой, которую я привел.

Если у тебя сиквенц последовательность. То значение можешь вообще 20 поставить :)
Там от хедера ничего не зависит. Только лишняя память. Worst Case всетаки.

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

Да, сорри, допишу документацию по этому параметру. Он единственный магический. Дефолт там 26 обычно его хватает.
По твоему кейсу я поставил

ha.init(20);

И HArrayInt выжило до 100 млн ключей

=== HArrayInt VS google::dense_hash_map VS std::map VS std::ordinary_map testing===
Insert/Search 10000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 169 msec, Search: 86 msec.

Insert/Search 20000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 372 msec, Search: 98 msec.

Insert/Search 30000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 509 msec, Search: 148 msec.

Insert/Search 40000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 686 msec, Search: 247 msec.

Insert/Search 50000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 850 msec, Search: 246 msec.

Insert/Search 60000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 1066 msec, Search: 296 msec.

Insert/Search 70000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 1184 msec, Search: 345 msec.

Insert/Search 80000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 1340 msec, Search: 386 msec.

Insert/Search 90000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 1533 msec, Search: 444 msec.

Insert/Search 100000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 1682 msec, Search: 493 msec.

По твоему кейсу я поставил

ha.init(20);

Ты вообще знаешь что этот параметр делает и как он работает, или рандомно подбираешь пока не заработает? :)

Это хорошо, что ты разобрался как сделать чтоб твоя структура не падала на 100 миллионах, потестил.

Теперь у меня твой код работате на 100 миллионов.

Я еще выяснил, что я по-ошибке неправильно считал время. Мой бенчмарк выдает total время (insert + lookup), а твой отдельно Insert и отдельно Lookup. Я недоглядял и брал только Lookup время из твоего вывода.

Вот поправил результаты:

keys count  rust hashmap ,insert  harrayint, insert	
10000000    64                    96
20000000    118                   208
30000000    180                   307
40000000    229                   386
50000000    315                   478
60000000    373                   582
70000000    412                   704
80000000    475                   790
90000000    571                   872
100000000   596                   985

keys count  rust hashmap, lookup  harrayint, lookup
10000000    24                    32
20000000    49                    72
30000000    74                    97
40000000    96                    128
50000000    125                   163
60000000    151                   190
70000000    170                   222
80000000    231                   255
90000000    237                   290
100000000   276                   309

HArray в среднем работает на 63% дольше на вставке и 28% дольше на lookup.

К сожалению не могу твой раст померять.
Получается твой раст работает в 2 раза быстрее чем dense_hash_map или unordered_map
Я в это слабо верю, в чемто есть подвох. Может он детектит что это просто монотонная последовательность и просто херачит в массив. Это нечестная игра синтетического теста.

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

Во-первых, сиквенц это ворст кейс для HArrayInt.
github.com/...​p_seq_32bits.png?raw=true
Во-вторых, ты сравниваешь структуры с сильно разной функциональностью. HArrayInt сортирует данные при вставки и предоставляет запросы типо вернуть ключи по ренжам, обойти сортированный список. Хештаблица ничего не сортирует и сохраняет как хеш пошлет.
Я в это слабо верю, в чемто есть подвох.

Лол. Разумеется, во всех тестах, в которых Бубен слил, есть какой-то подвох. Во всех тестах, где Бубен выиграл все было честно :)

Может он детектит что это просто монотонная последовательность и просто херачит в массив.

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

Но у меня есть тебе утешение. Rust использует LLVM на бекенде. Так же как и clang. Сделай так чтоб твой код компилился под clang и посмотрим, получит ли твой код такой же буст от оптимизаций или нет.

Я в это слабо верю, в чемто есть подвох. Может он детектит что это просто монотонная последовательность и просто херачит в массив. Это нечестная игра синтетического теста.

— Benchmarks are rigged!
— If you count benchmark legal results, we easily win. If you count illegal benchmark times, they can try to steal victory from us.
— We were winning in all the key benchmarks by a lot, actually. And then our number started miraculously getting whittled away in secret.
— ANY BENCHMARK THAT CAME IN AFTER JANUARY 24TH WILL NOT BE COUNTED!
— STOP THE PERFORMANCE TESTS!

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

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

При этом тот, кто тестит map на вставке 4-байтного ключа и значения жалуется на то, что тесты синтетические :)

То, что кол-во ключей заранее известно, позволяет получить еще лучше перформанс и это офигенно.

Ты лучше объясни почему твой код сливает и на вставке и на lookup?

Тю, не заметил. Так ты тут читишь
let mut map = HashMap::with_capacity_and_hasher(keys_count, NoopHasherBuilder {});

Передаешь размер хештаблицы в конструкторе.
Тоесть твой мап ОДИН РАЗ выделает большой кусок памяти на все элементы и сразу. Это синтетика.

В то время как HArrayInt не знает количества вставляемых элементов и честно растет в размерах (вызывая раз за разом тормознутый new()) в зависимости от того сколько элементов добавили.

Я изменил эту строчку, поставил сапасити 0 (в начале теста не известно количество вставляемых ключей) и твой Раст деградировал почти в 1.5 — 2 раза :)

play.rust-lang.org/...​65150cc6fd17bcdbc7a352f46

Если схитрить и передать в конструктор количество вставляемых ключей

let mut map = HashMap::with_capacity_and_hasher(keys_count, NoopHasherBuilder {});

Keys count: 0, elapsed ms: 0
Keys count: 1000000, elapsed ms: 27
Keys count: 2000000, elapsed ms: 54
Keys count: 3000000, elapsed ms: 80
Keys count: 4000000, elapsed ms: 110
Keys count: 5000000, elapsed ms: 137
Keys count: 6000000, elapsed ms: 159
Keys count: 7000000, elapsed ms: 185
Keys count: 8000000, elapsed ms: 208
Keys count: 9000000, elapsed ms: 299
Keys count: 10000000, elapsed ms: 245

Если не хитрить, и честно заставить хешмапу рости от количества вставляемых ключей как это делает HArrayInt

let mut map = HashMap::with_capacity_and_hasher(0, NoopHasherBuilder {});

Keys count: 0, elapsed ms: 0
Keys count: 1000000, elapsed ms: 41
Keys count: 2000000, elapsed ms: 84
Keys count: 3000000, elapsed ms: 101
Keys count: 4000000, elapsed ms: 200
Keys count: 5000000, elapsed ms: 189
Keys count: 6000000, elapsed ms: 205
Keys count: 7000000, elapsed ms: 220
Keys count: 8000000, elapsed ms: 341
Keys count: 9000000, elapsed ms: 359
Keys count: 10000000, elapsed ms: 519

Тоесть Хешмап в Расте проигрывает как по функциональности, так и по скорости HArrayInt при равных условиях.

Тю, не заметил. Так ты тут читишь
let mut map = HashMap::with_capacity_and_hasher(keys_count, NoopHasherBuilder {});

Передаешь размер хештаблицы в конструкторе.
Тоесть твой мап ОДИН РАЗ выделает большой кусок памяти на все элементы и сразу. Это синтетика.

В чем синтетика? Кол-во ключей известно заранее. Не вижу проблемы в том, чтоб задать его, если структура выиграет в перформансе от этого. У тебя тоже есть какие-то магические константы, которые нужно тюнить в зависимости от входных данных. Почему честно когда твой код это требует и нечестно когда мой код это требует?

В то время как HArrayInt не знает количества вставляемых элементов и честно растет в размерах (вызывая раз за разом тормознутый new()) в зависимости от того сколько элементов добавили.

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

В чем синтетика? Кол-во ключей известно заранее.

В большинстве задач неизвестно количество ключей.
Это как таблица в базе данных. Может быть 1 запись, а может быть 1 миллиард. Никто не создает таблицу с хинтом что там будет миллиард записей.

У тебя тоже есть какие-то магические константы, которые нужно тюнить в зависимости от входных данных. Почему честно когда твой код это требует и нечестно когда мой код это требует?

На этих магических константах мой код отжирает на порядки менььше памяти на старте чем поделка из раста

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

Никто не мешает твоей Хешмапе предоставить следующие Апи:
1. Скан ключей по диапазону
2. Скан ключей как сортированный список
3. Поиск ключа по подстроке
4. Удаление ключа с плавным освобождением памяти

Мы к этому вернемся. Ты сначала скажи, ты признаешь, что твоя поделка сливает Rust std::collections::HashMap на lookup?

Ты сначала скажи, ты признаешь, что твоя поделка сливает Rust std::collections::HashMap на lookup?

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

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

Тоесть ты своим синтетическим обманом забустил и инсерт и лукап. Еще мог бы ребилд хештаблице сделать после того как все элементы вставлены, забустишь еще больше.

Мы к этому вернемся. Ты сначала скажи, ты признаешь, что твоя поделка сливает Rust std::collections::HashMap на lookup?

Обьясняется тем что хештаблица в некоторых случах может быть делать меньше seek times. За счет того, что ключи разместила совершенно случайно, а не как сортированный список.

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

Мы к этому вернемся.

А что там возвращатся.
Читай список приимуществ HArray по сравнению с хештаблицами

1. Without any Stop World events such as Rebuild/Rehashing on Insert key.
2. Without any Hash Functions, the container has adpative algorithm for different nature of keys
3. Scan by Prefix/Scan by Range functionality as bonus
4. All Keys are sorted. Ability to set your Custom Order of Keys
5. Predictable behaviour even in worst case: smoothly consuming memory, almost constantly latency on insert/lookup
6. Prefix Compression helps reduce memory when keys have the same prefix: urls, file paths, words etc.
7. Serialize/Deserialize from/to file at a speed close to the max speed of the hard drive
8. Fair Delete operation with smoothly dismantling each Key. Dead fibres are used for insert new keys, so structure perfect works in massive insert/delete scenarios.

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

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

Поэтому, повторю вопрос:

Ты сначала скажи, ты признаешь, что твоя поделка сливает Rust std::collections::HashMap на lookup?

Ты так истеришь, как будто это ты лично эту хешмапу писал, а не какойто 50ти летний бородатый джедай, которому явно не нужно выпрашивать принципала. Ладно.

Вот тебе базовая адекватность. Еще на примере JudyArray (Trie) стало понятно, что в любой хештаблице можно подобрать такую хешфункцию, при которой JudyArray будет немного, но проигрывать по скорости хештаблице. При этом JudyArray (как и HArray, более совершенная версия) имеет другой список приимуществ, которые напрямую открывают путь в мир больших баз данных
dou.ua/...​rums/topic/18849/#2044934

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

До речі було б дивно якби не зливала... Просто HashMap має дуже мало обмежень в порівнянні з його HArray’єм. Просто в HashMap’і лук-ап взагалі повинен відбуватись за пару процесорних інструкцій (ну ... + колізії) — таке важко побороти.

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

«не все так просто» ©. В векторі reserve має сенс, в хеш-таблицях теж. В зв’язних списках і червоно-чорних деревах немає.

Я там вище написав що проблему великої кількості new() можна обійти зробивши можливість додати свій кастомний аллокатор ( en.wikipedia.org/wiki/Allocator_(C++ ), але перед тим треба би результати профайлу глянути.

В то время как HArrayInt не знает количества вставляемых элементов и честно растет в размерах (вызывая раз за разом тормознутый new())

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

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

Если бы я хотел сделать твой некостыль, для синтетических структур, я бы в конструкторе передал количество ключей, а потом типа понял что ты вставляешь числа последовательно и просто заполнил массив и сделал твою хешмапу в 5 раз по скорости. Заняло бы 10 строчек кода.

Но я так не сделал и написал максимально сбалансированную и универсальную структуру данных в 8к строк, которая выживает в любых боевых условиях. Которая ведёт себя максимально предсказуемо в разных корнер кейсах за О(1). Чего не скажешь о хештаблице с ее O(n) в худшем случае.

Чего не скажешь о хештаблице с ее O(n) в худшем случае.

Совершенно не обязательно: реализации хэш-таблиц, которые не делают перемещение всех ключей рывком, а делают это по 1-2 дополнительных ключа на каждую операцию добавления — достаточно распространены (например, в MSVC STL).

(возражение только в этом пункте)

Пока автор не выложит white paper, не о чем говорить, так как непонятно что именно и как много он выделяет и где можно оптимизировать, а по коду хрен разберет что он там наколбасил. Ему никто не мешает добавить возможность выделить всю память заранее и получить более высокий перфонмарс, если кол-во ключей известно (а оно известно), как это сделано практически в любом годном контейнере для элементов.

Там, где нужно выжать максимальный перформанс, выделение памяти заранее — это стандартная практика, так как кол-во памяти, которую можно выделить под структуру известно заранее и фиксировано, и оно ограничевает кол-во данных, которое можно будет хранить на конкретной машине, что в свою очередь диктует как будет выполняться масштабирование системы.

Насправді, для ліста та balanced BST, зазвичай, можна пам’ять виділити наперед і скористатись кастомними алокаторами, так шо new буде практично placement new (шо є no op у випадку інтів). За допомогою пулу, власне. Для trie така оптимізація теж, загалом, можлива, думаю і для даного trie теж. Тільки дещо морока це робити.

О. Я про це теж писав. Треба було мені перечитати все (лол) перед тим як самому вкидувати на вентилятор.

Не понял этот момент. Если принципиально структура построена так, что «превыделить» память невозможно (как LinkedList и BinaryTree), как можно ее выделить и получить буст к перформансу?

Например, в случае c linkedlist, если знаешь сколько нужно будет хранить элементов максимум, можно выделить заранее память под них большим куском и аллоцировать память оттуда кастомным мемпулом или чем-то аналогичным. В особо задроченых вариантах, без поддержки многопоточности, alloc (или free) можно будет сделать 2-3 инструкциями.

Или, например, std:set это не годный контейнер?

Не большой спец по С++, но быстрый поиск показывает, что std::set параметризируется типом аллокатора, т.е. при желании туда можно будет засунуть аллокатор на базе мемпула (если он там не встроен в конкретной реализации).

Вот пример:

referencesource.microsoft.com/...​/generic/dictionary.cs,61
referencesource.microsoft.com/...​generic/dictionary.cs,315

В хеш-таблице используется linked list для поддержки коллизий. Вместо аллокации элементов этого связного списка в куче (что будет медленно но уверенно херить кучу и усложнять работу сборщика мусора), память под все элементы выделяется одним большим блоком заранее и будет расходоваться по мере наполнения хеш-таблицы данными.

Це оптимізує тільки виділення/видалення елементів. Тому що виділити та видалити пам’ять це, загалом, нетривіальна операція. На пошук воно не дуже вплине, хіба що більш щільне розташування елементів може призвести до меншої кількості cache miss.

Понимаешь, какая штука: мозг умеет обрабатывать будущие события — подготавливает в голове реакции на разные альтернативные события — несколько вариантов, а твой процессор нет. А должен бы, ведь в ограниченной модели легче, чем в мозгу. Правда у мозга много нейронов, а у процессора с гулькин нос.
Извини, не удержался подразнить немного...

З linked list можна зробити фокус коли певна кількість елементів живе у нерозривному блоку пам’яті і при необхідності додається новий блок. Тобто всередині замість щоб кожен елемент вказував на наступний більша кількість елементів йде послідовно.

Морока буде позначати елементи як видалені і робити кастомний ітератор.

Тут проблема ИМХО в понимании области применения. По сути связный список необходим только для того, чтобы организовать в FIFO/LIFO очередь (например, события различные) некоторые элементы при условии, что мы не будем по ним бегать туда-сюда, тогда расположение в памяти элементов не имеет значения, последовательно или рандомно, мы работаем с одним элементом и может перейти к следующему или пушнуть элемент в голову списка. Если в списке оказываются больше дюжины элементов одновременно, то что-то пошло не так и организация данных выбрана неправильно.

Тут проблема ИМХО в понимании области применения.

Наприклад задача видавати ID з пулу. Їх можна повертати у довільному порядку назад в пул і гарантовано буде «переповнення» коли треба буде використовувати раніше видані, але вже звільнені ID. І якщо треба зробити за менше ніж O(n) часу та бажано менші ніж O(n) пам’яті то така модифікація списку блоками одночасно і проста для реалізації і дає виграш в швидкості як мінімум.

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

не читал вашу дискуссию но обсуждаю) en.wikipedia.org/wiki/Free_list

Это не будет работать в С++ с кучей мелких new вызовов. В стандартных библиотеках используют арены — это кусок памяти небольшого размера (64-1024Кб) в котором происходят аллокации. Как только память заканчивается, выделяется новая арена. Внутри арена может быть разбита на bit buckets, а не просто списки и т.д. Чистый список — это самоубийство.

Вы же там говорили про выделение хендлов. Вот как раз можно завести список свободных хендлов.
Пулы памяти хороши (правда, мне от них страшно) когда приходит момент, в который этот можно сбросить — то есть, программа выполняет циклические действия, вроде обработки запросов.

Для хэндлов хороши битмаски, например реализация IDR/IDA хендлов линуксовом ядре очень приятно сделана + если лицензия позволяет — оно отрывается от ядра линукса на раз-два-три и можно использовать в своих проектах:
www.kernel.org/...​/latest/core-api/idr.html

По поводу пулов памяти, опять же мне очень нравится реализация kmem_cache***() в линуксовом ядре, всё очень продуманно, есть даже конструкторы объектов в кеше/пуле, что инициализировать элементы перед тем как отдавать юзеру. Причём я не знаю кто был первым, но в ядре Solaris есть точно такое же API:

Solaris: docs.oracle.com/...​kmem-cache-create-9f.html
Linux: www.kernel.org/...​rstand/understand025.html

что говорит о том, что для мелких объектов реализация очень успешная получилась.

Спасибо, но мне как плюсовику-затейнику сложно(
Отложу пока не прийдется самому его писать

Как это повлияет, например, на перфоманс того же lookup?

На lookup в теории влияние будет слабое. Например, раз все узлы linked list аллоцированы из одного блока памяти, будет выше шанс на CPU cache hit или на TLB cache hit, чем если они аллоцировались из рандомных мест в куче втечене жизни программы, но все это сильно зависит от типа нагрузки и патерна доступа к данным.

Наибольший эфект будет проявляться в местах, где аллоцируется/освобождается память.

И на языки без сборки мусора, например С++?

Вполне позитивно повлияет, так как вместо аллокации/освобождения большого кол-ва маленьких блоков памяти (что имеет условно константные затраты по времени вне зависимости от размера блока) будет одна аллокация большого блока памяти. в начале работы программы. Ну и не будет фрагментации кучи, что тоже плюс.

На lookup в теории влияние будет слабое. Например, раз все узлы linked list аллоцированы из одного блока памяти, будет выше шанс на CPU cache hit или на TLB cache hit

Для linked list будет очень сильное влияние, т.к. процессор не может предсказать рандомные обращения к памяти, а вот последовательные аж бегом. А вообще, парни, вы идёте правильной дорогой, не ожидал столько правильных мыслей в этом треде. Ещё чуть-чуть и будет понимание почему Интел аж в 1995 в Pentium Pro добавил поддержку 2Mb страниц вместо 4Kb.

Таблицы для MMU находятся в таких же страницах памяти и если аллокаций по 4Kb блоков будет много, то каждое последовательное выделение памяти и добавление её в адресное пространство процесса будет всё медленнее и медленнее из-за растущих таблиц MMU.

Із цікавого — код читабельний, але взагалі не перегружений коментарями і чуваки схоже взагалі не чули про стайл чекери. Із плюсів — взагалі немає єрунди, коли заставляють використовувати такий стиль:

if (...)
{
    return 1;
}
else
{
    return 2;
}
— ну от не хочу я ультравайд вертикально ставити щоб з таким кодом працювати, пробачте.

І взагалі ... народ любить писати return в тому ж рядку, що і if:

if (...) return 1;
return 2;

Але те, що в коді деколи попадається пробєл після відкриваючої дужки в if (рядок 425), або те, що народ від фонаря ставить пробєли навколо == трохи дивує :).

Але те, що в коді деколи попадається пробєл після відкриваючої дужки в if (рядок 425)

Там просто по рукам мало били, нет общего стиля.

або те, що народ від фонаря ставить пробєли навколо == трохи дивує :).

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

Я часто ставлю пробєли як логічне групування:

d = b*b - 4*a*c
место аллокации элементов этого связного списка в куче (что будет медленно но уверенно херить кучу и усложнять работу сборщика мусора)

Це не тільки для цього зроблено :-). Більшою мірою для того, щоб можна було пройтись по всіх елементах мапи за «не дуже дорого».

Власне є такий жарт, що якщо ти маєш популярний продукт, то implementation стане specification. Тобто навіть якщо ти явно кажеш, що щось там unspecified/undocumented, то всерівно найдеться народ, який буде покладатись на цю фішку. І навіть не зважаючи на те, що народ явно робить єрунду, вони потім зляться коли їхня прога глючить.

Починаючи з якоїсь версії пітон реалізовував dict так, що ітерація по dict’у повертала ключі в тому порядку, в якому їх вставляли. Народ почав настільки полагатись на цю штуку, що її прийшлось додати цю фішку в специфікацію (починаючи з 3.7). І тепер прийдеться завжди тягнути це за собою.

Автори Го настільки люблять свою свободу, що самі шафлять ключі, щоб якийсь мудак не почав покладатись на якісь деталі реалізації:
play.golang.com/p/ev19NzZv0gD

вызывая раз за разом тормознутый new()

Так ето. Ти б результати профайлу привів, їй богу. Просто говорити що на new() йде багато часу без профайлу якось тупо, імхо. Просто якщо виявиться що new() займає 5%, то якось буде неловко.

По-друге зроби як всі людські контейнери СТЛ — прийми додатковим параметром (можна параметром шаблону) аллокатор. Це тобі дасть змогу наперед виділити N*sizeof(Node) байт під твої вузли і тоді, при потребі, new буде працювати дуже швидко. Знову ж таки перед тим як займатись цією штукою було б непогано показати результат профайлу — куди час зараз йде?

Пофиксил error.
Впринципе это какбы кновн ишью, пул закончился.
Поставил константу 2048 вместо 1024
const uint32 INIT_MAX_PAGES = 2048;

Позже надо будет авто resize прикрутить, для любителей засунуть от 70 млн секвенц ключей и потестить Worst Case HArrayInt

Я сделал git pull, проверил что INIT_MAX_PAGES теперь 2048, пересобрал, все равно падает.

Insert/Search 70000000 SEQUENCE keys (4 bytes each) ...
HArrayInt =>
C:\src\HArray\Release\HArray.exe (process 52992) exited with code -1073740791.
Press any key to close this window . . .

Пофикси багу и проверь что пофиксилось, выложи вывод программы, потом я еще раз попробую.

Я предлагаю тестить на датасетах до 50 млн ключей.
На 50 млн и выше у меня и стандартные либы (std::map, std::unordered_map)
с аутофмемори мрут.
А HArrayInt мрет гдето на 80 млн ключей, на моей машине.

Я вижу, что мрет. Наверное, надо разобраться почему? 80 миллионов ключей + значений каждое по 4 байта это 640МБ. Сколько дополнительно твоя структура жрет, что хранение такого кол-ва данных в памяти это проблема на моем компьютере с 64ГБ памяти?

Я вижу, что мрет. Наверное, надо разобраться почему? 80 миллионов ключей + значений каждое по 4 байта это 640МБ.

Забыл про 4 байта на велью. Это 1.3 гб. Оверхед гдето х2-х3 также как у хештаблицы.

Сколько дополнительно твоя структура жрет, что хранение такого кол-ва данных в памяти это проблема на моем компьютере с 64ГБ памяти?

Проблема что ты сбилдил как х32 битное приложение. А ему не отдадут больше 4 гиг.
Я только что сбилдил как х64, легко влезло 130 млн ключей.
Когдато помню архитектор с канады тестил, у него сказал влезло 1 млрд ключей.
Но там какаято непростая машинка была. Не ноут с 16 гб озу как у меня.

Проблема что ты сбилдил как х32 битное приложение. А ему не отдадут больше 4 гиг.

Я уже писал тебе, что в настройках сборки выбрал Release/x64.

Твой код падал задолго до того как достигает 4 гигов памяти.

Но там какаято непростая машинка была. Не ноут с 16 гб озу.

У меня тоже непростая машинка

Забыл про 4 байта на велью. Это 1.3 гб. Оверхед гдето х2-х3 также как у хештаблицы.

Сколько будет 80000000 * (4 + 4) ?

Эх, жаль, такой эпик срач пропустил. ТС, чем там сердце успокоилось? Допилил свой алгоритм? Если че, могу помочь с оптимизацией и кросс-платформенностью.

С этим алгоритмом проблем нет, он допилен и портирован и под линукс и под виндовс. Конечно нет предела совершенству, можно потратить и добрать в бенчмарках еще 10-20-30%, но сейчас это уже не интересно. Сейчас интересно этот же алгоритм адаптировать под хранение джисонов в базе данных. Там очень жирная теория получается, если в Trie хранить Json. Сжатие в 20-30 раз по дефолту за счёт инвертации данных, вставки секций джисона не приводят к его полной перезаписи, транзакции с блокировкой на уровне атрибутов и др. К сожалению времени очень не хватает, поэтому я только теорией занимаюсь, код не пишу. Хочу применить весь опыт и наработки, продумать и просчитать каждую деталь. Поэтому если интересно, писать ничего не нужно, есть алгоритмические задачи на подумать.

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

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

Дописал чесную функцию delete.
Если процесс вставки ключей в чемто напоминает процесс эволюции,
где свою жизнь ключ начинает как предельно простая примтивная конструкция,
постепенно усложнаяясь, разветвляясь и «эволюционируя» в серию блоков,
то при удаление идет полностью обратный процесс. Процесс деградации и распада на примитивы, которые в свою очередь могут быть использованы для построения новых структур (ключей). Таким образом если количество insert больше чем операций delete или их количество примерно равно, событие Stop World не происходит. Если количество операций delete значительно превышает количество операций insert, то контейнер в какойто момент определяет что может высвободить не менее 5% памяти и запускает процесс сборки муссора/дефрагментации, после которого отдает высвобожденные страницы памяти ОСи.

Также добавил возможность сериализации/десериализации на диск
и возможность задавать кастомные сортировки в структуре.

Добавил побольше красивых графиков и таблиц
github.com/Bazist/HArray

Ну вот както так, для бегинерз если основная идея
github.com/...ter/Trie_for_beginners.md

Пока что драфт, потом подправим.

Небольшой апдейт по проекту.
1. Унифицировал проект, теперь он компилится одинаково хорошо под Linux и под Windows из одних и тех же исходников.

2. Попытался с наскоку ввести элементы прямой адрессации. Потратил несколько вечеров чтобы сделать пруф-оф-концепт, неосилил. Точней осилил, но версия работает медленее чем текущая. Пока откладываем, но в уме держим, что всетаки теор. предел еще не достигнут.

3. Имел честь на прошлой неделе пободаться с Гордостью Всея Гугла — dense hash map. Надо сказать что в немалом количестве кейсов всетаки слил, на глаз счет 50/50 по всей той простыне бенчмарков. Конечно это был не совсем равноценный замер. и можно было сразу послать денсу в лес, поскольку мой Trie это честное поедсказуемое отсортированое дерево. Отсюда сканы по диапазону ключа, функции min max, префиксное сжатие, ровное наращивание памяти, отсудствие катастрофических worst cases (в некоторыъх случаях dense просто подвисала намертво, не так то просто подобрать хешфункцию). Из-за своей непредсказуемости и скудного функционала использовании хештаблиц в базах данных практически не имеет смысла.

4. Но я не сдался ! Кручу в голове потихоньку и прицениваюсь к созданию вариатора размера блоков. Это займет какоето время. В теории всеравно хорошо оптимизированое Trie должно работать быстрее чем Хештаблицы и уж тем более чем любые подвиды бинарных деревьев.

Думаю что с HArrayInt ложная тревога и слухи. Я вспомнил что один в один его передирал в менеджед дотнет язык и если бы там было что-то не чисто с bounds уже бы давно выяснилось

Меж тем, этим вечером я в целях более глубокого освоения Rust решил реализовать на нём какую-нибудь интересную структуру данных. В качестве таковой был выбран HAT-Trie с некоторыми моими изменениями в алгоритме. Матан по HAT-Trie здесь: crpit.com/...apers/CRPITV62Askitis.pdf Свои изменения я опишу, если кому-то будет интересно. За сегодняшний вечер получены результаты для 32-разрядных ключей и значений:
[zenom@lynx release]$ ./rust_playground Size 1000000 Insert 0 sec 125162718 nanos Search 0 sec 102876360 nanos Size 2000000 Insert 0 sec 258873856 nanos Search 0 sec 221173375 nanos Size 3000000 Insert 0 sec 392696414 nanos Search 0 sec 305683095 nanos Size 4000000 Insert 0 sec 547442910 nanos Search 0 sec 454124008 nanos Size 5000000 Insert 0 sec 698278097 nanos Search 0 sec 532422106 nanos Size 6000000 Insert 0 sec 866163393 nanos Search 0 sec 716194667 nanos Size 7000000 Insert 1 sec 41583520 nanos Search 0 sec 784532907 nanos Size 8000000 Insert 1 sec 168931976 nanos Search 0 sec 920360498 nanos Size 9000000 Insert 1 sec 357707590 nanos Search 1 sec 13637407 nanos
Оверхед по памяти:
[zenom@lynx release]$ /bin/time -f "Max resident %M KiB" ./rust_playground > /dev/null Max resident 191984 KiB
Никакой чёрной оптимизирующей магии пока не применял, писал более-менее идиоматичный Rust-код (с поправкой на то, что сам язык я только осваиваю). Если сообществу будет интересно, то могу после стабилизации, генерализации и возможной оптимизации кода опубликовать исходники, библиотеку и C-биндинги к ней.

Извиняюсь, форматирование полетело. Результаты бенчмарков: pastebin.com/Xft1uRA8

Давайте введем единицу измерения Один Идеал.
Один Идеал равен скорости работы моего алгоритма.
Если алгоритм сливает всего 2-3 раза идеалу, мы будем называеть его почти идеалом.
В 3-10 раз Микрософт-Линупс эдишн
10+ раз Сырной эдишин.

Итак. Насколько ваш алгоритм медленее работает Идеала ?

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

Конечно посмотрел код, куча вопросов, наприклад:

void reallocateContentPages()
{
............
for (; j < newSizeContentPages; j++)
{
pTempContentPages[j] = 0;
}
............
}
вот тут цикл обязательно гонять, мемсетом никак ?

----------------------------------------------------

catch (...) //maybe out of memory exception
{
destroy();

throw;
}

Если мы все мега оптимизируем, то исключения обязательно юзать?
Не знаю как в других компилерах, но жцц точно может 0 возвращать в случае ошибки в new, если какая то хренька с памятью.

Мемсет я вообще выпилю со своего кода. Чем проще код тем лучше, тем меньше головняка на разных платформах. For наше все

Кстати, забавный факт. Требуется очистить 32Mb некешеруемой памяти, установить там 64битовый паттерн (аналог PTE). Супер-пупер оптимизированный интелом memset сделал это за 180ms. Поделив эту память просто на 4 куска и вызвав 4 потока через pthread_create() на четырёхпроцессорной машине время очистки и завершения четырёх потоков составило 46ms. Операция очистки и установки паттерна делалась через тип double, чтобы записывать 64 бита за раз атомарно.

Дохлые процессоры — замена Intel Atom’у :) ark.intel.com/...me/80644/Apollo-Lake#@All Я не знаю, они вышли в продажу или ещё нет.

Я специально написал, что память не кешированная (замаплена с флагом PROT_NOCACHE, т.к. шарится с устройством).

99%, что

Супер-пупер оптимизированный интелом memset

был рассчитан именно на кэшируемую память.

А на некэшируемой обычно есть какой-то жёстко требуемый размер передачи на одну операцию, и его надо соблюдать.

4 потока через pthread_create()

Думаю, однонитевый вариант, но с 4мя подряд store, сработал бы не сильно хуже.

С кешируемой используется prefetch, но он далеко не всемогущь. Поэтому четыре юнита, каждый со своим префетчем был бы всё равно эффективнее (может не в четыре, а 2.5-3 раза).

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

Кстати, может дашь ссылку на реализацию алгоритмов оптимизации с поддержкой многопоточности на плюсах и не за бабки, а еще лучше для opencl. А да, интересуют от стандартных (с производной и без), а также SA и GA, понятно, что это все не для одной переменной.
Если честно, то без понятия, я в этих кругах не вращаюсь, всё больше обработка звука и изображений.
А что там со звуком.
Много чего, в основном реализация а ля ClearTalk.
А вот я сильно удивился, когда оказалось, что c оптимизационными алгоритмами всё хуже. Не, теория расписана вдоль и поперек, а вот отлаженных и говтовых реализаций кот наплакал. Все лепят свои велосипеды с разной степенью глючности.
Я только симплекс метод реализовывал для решения, когда докторскую делали, но это было давно и неправда.
Поэтому четыре юнита, каждый со своим префетчем был бы всё равно эффективнее

Зависит от соотношения скорости памяти и шины процессор — контроллер памяти.

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

Видимо, это у тебя вокруг седые дядьки с 30 годами стажа на 8051 :) В моих краях уже нормально «походя» применить какой-нибудь parallel_foreach (в чём-то эквивалент того, что ты описал), ну а пачки слегка конкурирующих async/promise-based агентов — регулярно выскакивают. Но это не причина параллелить вообще не проверяя, что получится :)

В моих краях уже нормально «походя» применить какой-нибудь parallel_foreach
TBB используете? Завидую чёрной завистью :)

Перемеряй на QNX, все баги связаные с 64 разрядными регистрами должни были уйти.
github.com/Bazist/HArray.UNX

Мои поздравления, работает, но стек ты где-то порешь. Я увеличил размер стека по дефолту до 16Mb, чтобы оно не задевало stack guard — ты использовал 772К из 16Mb, что довольно таки много, ну а про 767Mb RAM уже и говорить нечего. Это в среднем, иногда 300Mb, иногда 1300Mb.

491547 1 ./ha64_g 10r RUNNING 0 767M 772K(16M)*

Ну а на последок держи сладкую пилюлю — скорость работы под QNX, std::map используется от LLVM C++, она тормознутая очень:

# ./ha64_g
=== HArrayInt VS std::map<int,int> testing ===
Insert/Search 1000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 13 msec, Search: 4 msec.
std::map => Insert: 342 msec, Search: 126 msec.

Insert/Search 3000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 45 msec, Search: 11 msec.
std::map => Insert: 1146 msec, Search: 385 msec.

Insert/Search 5000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 75 msec, Search: 19 msec.
std::map => Insert: 1972 msec, Search: 534 msec.

Insert/Search 7000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 100 msec, Search: 33 msec.
std::map => Insert: 2669 msec, Search: 758 msec.

Insert/Search 9000000 SEQUENCE keys (4 bytes each) ...
HArrayInt => Insert: 220 msec, Search: 38 msec.
std::map => Insert: 3634 msec, Search: 1231 msec.

Insert/Search 1000000 RANDOM keys (4 bytes each) ...
HArrayInt => Insert: 14 msec, Search: 6 msec.
std::map => Insert: 322 msec, Search: 312 msec.

Insert/Search 3000000 RANDOM keys (4 bytes each) ...
HArrayInt => Insert: 33 msec, Search: 19 msec.
std::map => Insert: 805 msec, Search: 804 msec.

Insert/Search 5000000 RANDOM keys (4 bytes each) ...
HArrayInt => Insert: 53 msec, Search: 31 msec.
std::map => Insert: 1350 msec, Search: 1340 msec.

Insert/Search 7000000 RANDOM keys (4 bytes each) ...
HArrayInt => Insert: 115 msec, Search: 68 msec.
std::map => Insert: 2215 msec, Search: 2143 msec.

Insert/Search 9000000 RANDOM keys (4 bytes each) ...
HArrayInt => Insert: 94 msec, Search: 56 msec.
std::map => Insert: 2397 msec, Search: 2651 msec.

Insert/Search 1000000 PERIOD keys (4 bytes each) ...
HArrayInt => Insert: 23 msec, Search: 4 msec.
std::map => Insert: 339 msec, Search: 100 msec.

Insert/Search 3000000 PERIOD keys (4 bytes each) ...
HArrayInt => Insert: 93 msec, Search: 13 msec.
std::map => Insert: 1089 msec, Search: 311 msec.

Insert/Search 5000000 PERIOD keys (4 bytes each) ...
HArrayInt => Insert: 119 msec, Search: 21 msec.
std::map => Insert: 1953 msec, Search: 658 msec.

Insert/Search 7000000 PERIOD keys (4 bytes each) ...
HArrayInt => Insert: 164 msec, Search: 29 msec.
std::map => Insert: 2669 msec, Search: 933 msec.

Insert/Search 9000000 PERIOD keys (4 bytes each) ...
HArrayInt => Insert: 217 msec, Search: 38 msec.
std::map => Insert: 3509 msec, Search: 993 msec.

=== HArrayVarRAM VS std::map<binkey,int> testing ===
Insert/Search 1000000 SEQUENCE keys (16 bytes each) ...
HArrayVarRAM => Insert: 145 msec, Search: 125 msec.
std::map => Insert: 795 msec, Search: 331 msec.

Insert/Search 3000000 SEQUENCE keys (16 bytes each) ...
HArrayVarRAM => Insert: 535 msec, Search: 331 msec.
std::map => Insert: 2694 msec, Search: 827 msec.

Insert/Search 5000000 SEQUENCE keys (16 bytes each) ...
HArrayVarRAM => Insert: 615 msec, Search: 500 msec.
std::map => Insert: 4102 msec, Search: 1441 msec.

Insert/Search 7000000 SEQUENCE keys (16 bytes each) ...
HArrayVarRAM => Insert: 996 msec, Search: 777 msec.
std::map => Insert: 6388 msec, Search: 2594 msec.

Insert/Search 1000000 RANDOM keys (16 bytes each) ...
HArrayVarRAM => Insert: 154 msec, Search: 104 msec.
std::map => Insert: 1229 msec, Search: 940 msec.

Insert/Search 3000000 RANDOM keys (16 bytes each) ...
HArrayVarRAM => Insert: 348 msec, Search: 327 msec.
std::map => Insert: 4461 msec, Search: 3611 msec.

Insert/Search 5000000 RANDOM keys (16 bytes each) ...
HArrayVarRAM => Insert: 611 msec, Search: 669 msec.
std::map => Insert: 7979 msec, Search: 6768 msec.

Insert/Search 7000000 RANDOM keys (16 bytes each) ...
HArrayVarRAM => Insert: 1103 msec, Search: 1254 msec.
std::map => Insert: 11952 msec, Search: 9912 msec.

Insert/Search 1000000 PERIOD keys (16 bytes each) ...
HArrayVarRAM => Insert: 41 msec, Search: 24 msec.
std::map => Insert: 395 msec, Search: 227 msec.

Insert/Search 3000000 PERIOD keys (16 bytes each) ...
HArrayVarRAM => Insert: 203 msec, Search: 117 msec.
std::map => Insert: 1237 msec, Search: 483 msec.

Insert/Search 5000000 PERIOD keys (16 bytes each) ...
HArrayVarRAM => Insert: 299 msec, Search: 122 msec.
std::map => Insert: 2196 msec, Search: 828 msec.

Insert/Search 7000000 PERIOD keys (16 bytes each) ...
HArrayVarRAM => Insert: 285 msec, Search: 171 msec.
std::map => Insert: 3447 msec, Search: 1721 msec.

=== HArrayVarRAM VS std::map<strkey,int> testing ===
Insert/Search 1000000 SIMILAR keys (64 bytes each) ...
HArrayVarRAM => Insert: 355 msec, Search: 461 msec.
std::map => Insert: 4686 msec, Search: 3333 msec.

Insert/Search 2000000 SIMILAR keys (64 bytes each) ...
HArrayVarRAM => Insert: 842 msec, Search: 1135 msec.
std::map => Insert: 9222 msec, Search: 8796 msec.

Insert/Search 3000000 SIMILAR keys (64 bytes each) ...
HArrayVarRAM => Insert: 1780 msec, Search: 1694 msec.
std::map => Insert: 14467 msec, Search: 14783 msec.

Insert/Search 4000000 SIMILAR keys (64 bytes each) ...
HArrayVarRAM => Insert: 2175 msec, Search: 2695 msec.
std::map => Insert: 19795 msec, Search: 21385 msec.

Insert/Search 5000000 SIMILAR keys (64 bytes each) ...
HArrayVarRAM => Insert: 3237 msec, Search: 3451 msec.
std::map => Insert: 25601 msec, Search: 26373 msec.

Insert/Search 1000000 RANDOM keys (64 bytes each) ...
HArrayVarRAM => Insert: 338 msec, Search: 364 msec.
std::map => Insert: 1717 msec, Search: 1605 msec.

Insert/Search 2000000 RANDOM keys (64 bytes each) ...
HArrayVarRAM => Insert: 937 msec, Search: 922 msec.
std::map => Insert: 3473 msec, Search: 3600 msec.

Insert/Search 3000000 RANDOM keys (64 bytes each) ...
HArrayVarRAM => Insert: 1500 msec, Search: 1548 msec.
std::map => Insert: 5896 msec, Search: 5495 msec.

Insert/Search 4000000 RANDOM keys (64 bytes each) ...
HArrayVarRAM => Insert: 2279 msec, Search: 2150 msec.
std::map => Insert: 8228 msec, Search: 7881 msec.

Insert/Search 5000000 RANDOM keys (64 bytes each) ...
HArrayVarRAM => Insert: 2981 msec, Search: 2830 msec.
std::map => Insert: 10440 msec, Search: 10046 msec.

Спасибо. А можно как-то вычислить что именно с стеком ? И кто именно порит, HArrayInt или HArrayVarRAM ? Это просто размера стека не хватило ?

Нет, не просто не хватило, а что-то вылезло за пределы (пусть даже на 4/8 байта — guard’у пофиг — он пристреливает процесс. Если бы это было легально расширение по типу push или alloca(), то гард бы доаллоцировал ещё 128Mb и сдвинул бы себя в конец 512+128 и так далее, в принципе до тех пор пока стек на наедет на код или данные.

Определить достаточно тяжело, мы портировали один Linux продукт, так у них там таймер заводился на стеке, а обрабатывался таймер в другом потоке, который получал указатель на стек другого потока. Как оно работало под Linux я не знаю, но под QNX мы получали страшные вещи, например, завели таймер, но выполнили задачу раньше и вышли из функции, но таймер не остановили и потом приходит прямая запись в стек из другого потока, что срок таймера вышел, а там в это время работает другая функция и ей трут стек. Так вот никакие valgrind, mudflap, sanitize не помогали. Только когда мы обвернули каждую запись по указателю в макрос, в котором проверяли не пишем ли мы в стек, только тогда нашли где проблема. В твоём случае по-проще конечно, просто старайся меньше использовать стек, используй заранее выделенную при инициализации память для своих внутренних нужд, пусть она и временная. ты всё равно пожираешь память гигабайтами, +/- мегабайт ничего не решит.

Ну Ок. Если нельзя определить это дебаг средствами, то можно хотябы грубо локализовать проблему.
1. УБРАТЬ из теста полностью работу с std::map дабы на 100% гарантировать что дело не в ней.
2. Оставить ТОЛЬКО HArrayInt в бенчмарках и протестировать.
3. Оставить ТОЛЬКО HArrayVarRAM в бенчмарках и протестировать.
Хотябы так, тогда может появится зацепка на что смотреть.
Под Ubuntu или Windows у меня пока нет вообще идей что это можно хоть както воспроизвести.
PS: Чтобы память не жрало много, можно уменьшить число в функции init. Поставить 20, например, вместо 24 или 26. Общий алгоритм примерно таков. 2^headerBase*4 = предвыделенное количество памяти

2. Оставить ТОЛЬКО HArrayInt в бенчмарках и протестировать.
По ходу валится гораздо раньше вот тут:

void init(uint headerBase)
{
memset(this, 0, sizeof(HArrayInt));

1) C++ класс — это не просто структура с данными.
2) sizeof(*this) и sizeof(HArrayInt) могут отличаться.
3) Сделай метод clear() и в нём очисть все данные вручную.

Понял, вечером закомичу изменения.
PS: Забавно, в том «спагетти» коде с гоуту, которое составляет ядро алгоритма, меньше багов чем в перефирийном отрефактореном коде :)

Нет, там дикая смесь самопала и потрохов около-tensorflow/etc. библиотек, присыпанных сверху слоем Lua, а в куче мест ещё и kdb+ (это чтобы жизнь мёдом не казалась даже уйдя с работы).
Увы. TBB это из серии того покоя, который только снится.

вот тут цикл обязательно гонять, мемсетом никак ?

Там рядом memset закомментированный => что-то с ним было не то. Можно уточнить, что именно. Возможно, какие-то виндовые тараканы.

Не знаю как в других компилерах, но жцц точно может 0 возвращать в случае ошибки в new, если какая то хренька с памятью.

Что-то сомневаюсь. Если это и было, то должны были давно исправить.

1. Речь про умолчательную форму new, а не специальную nothrow.
2. Я верю, что ты не видел граблей с memset. Поэтому вопрос к ТС.

вот тут цикл обязательно гонять, мемсетом никак ?
Там рядом memset закомментированный => что-то с ним было не то. Можно уточнить, что именно. Возможно, какие-то виндовые тараканы.

На Линуксе были проблемы с мемсетом. Почему именно, не скажу, но когда убрал мемсет и заменил на обычные циклы — все заработало.

На Линуксе были проблемы с мемсетом. Почему именно, не скажу, но когда убрал мемсет и заменил на обычные циклы — все заработало.
Какие проблемы могут быть с memset’ом? %)

gcc использует билтин мемсет:

#include <stdint.h>                        // for uintptr_t
#define inline_memset(dst, c, N)                            \
   __asm__ __volatile__(                                              \
       "rep stos %1, (%0)\n\t"                                        \
       :: "D"((dst)), "a"((uintptr_t)(c)), "c"((N)/sizeof(uintptr_t)) \
       : "memory");

Проблемы обычно с руками, использующие мемсет...

gcc использует билтин мемсет:

Для фиксированного размера 16 — gcc породил два movq.

Для 100 — нечто длинное и сложное команд на 30, в котором, конечно, есть rep stosq, но только одна из всего этого набора. Остальное похоже на пляски вокруг невыровненного случая.

Для произвольного внешне заданного размера — три команды, последняя — jmp memset.

Всё это на gcc 4.8.4 для x86-64.

Твоё представление о gcc меня таки удивляет. Это в QNX такая версия? Сколько ей лет?

Там много паттернов, особенно, если ты даёшь выравненные на 8 данные, то она становится именно двумя командами. Увы, для остальных операционок — это мечта %)

Увы, для остальных операционок — это мечта %)

Ну вот что-то не видно тут зависимости от ОС. От компилятора — да, от ISA.
OK, thx, вопрос закрыт.

Можливо тому що у Вас:

	memset(pContentPages, 0, contentPagesSize * sizeof(uint)); 

а краще було б якось так:

 	memset(pContentPages, 0, contentPagesSize * sizeof(ContentPage*)); 

На x64 буде різниця.

Заодно открой для себя uintptr_t.

Очень рад за тебя, что ты нашел для себя легкую жертву в ньюансах линукс платформы. Теперь можешь открывать шампанское и праздновать, что у когото глаза не достаточно красные чтобы учесть все ньюансы этого линукс зоопарка. Особенно радует твой шах и мат с QNX, ведь я туда скорей всего никогда не доберусь и можно писать мне любые issue. Особенно когда тебя просят чтото конкретное, но ты конечно — ну тыпонел.

PS: Пойду на москальском форуме попасусь, может там ребята поскромнее.

Очень рад за тебя, что ты нашел для себя легкую жертву в ньюансах линукс платформы.
Хоть раз прочти C++11 или C99 стандарт от начала и до конца. Причём тут линукс?

Ой не свисти. Мне нужно было портировать с Виндовс под Линукс, я сел и за 20 мин это сделал. Там все достаточно просто и по стандартам, с некоторыми ньюансами на ваш overcommit. А вот сможешь ли ты свой любой проект из под Линукс портировать под Виндовс — большой вопрос.

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

А вот сможешь ли ты свой любой проект из под Линукс портировать под Виндовс — большой вопрос.
Ещё больший вопрос, что я знаю лучше винду или линукс — для себя я так и не определился.

Последний из известных opensource проектов, где я принимал участие — OpenAL Soft ( kcat.strangesoft.net/openal.html ) дописывался и писался исключительно на Windows, под другими операционками были только sanity checks.

При том, что MSVC начал стараться поддерживать C99 только в 2015-й версии. Так что для винды это, таки новости.

Вон в su.c-c++ коллеги драйверописатели задумчиво обсуждают, чи не перейти на 2008 или 2010 версию со старой освоенной 2005, и сходятся на том, что таки пока облом.
А ты говоришь — C++11...

Хотя какие-то подвижки есть — VS2015upd3 таки выкатило, говорят, компилятор с SSA(!), теперь ждём новых удивительных историй...

Внимательней читайте код. Везде используется uint
#define uint uint32_t
#define ulong uint64_t
#define ushort uint16_t
#define uchar uint8_t
#define ucode uint8_t

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

так прекрасно вбивается в бошку, что указатель — это указатель, а никак не int, long и т.п.
Как раз вспоминался на днях Watcom/DOS4GW с 48 битовыми указателями — селектор+смещение %)

Кстати, что в нём буква W значит?
А то я одно время любил вытаскивать DOS4G.EXE из Heretic и подкладывать под борландовый выхлоп — оно местами даже чуть лучше работало :) но за полным отсутствием документации по источникам этого безобразия осталось непонятым, а потом я про него забыл на много лет.

Кстати, что в нём буква W значит?
W — это free re-distributive, коммерческая стоила около 200 баксов за рабочее место, она предоставляла полную версию DOS PM Extender’а, согласно спецификации, которая по большому счёту никому не нужна была, ибо там были очень специфические вещи, такие как перехват обращения к регистрам, памяти и прочее.

Понятно, спасибо.

Посыпаю голову пеплом.
Действительно на той конфигурации на которой тестировал,
64х битные указатели

Ваша структура надає інтерфейс для видалення елемента по ключу?

Только обнуление элемента.
Тоесть массовое удаление не приводит к шринканью самой структуры. Но с временем можно сделать rebuild и шринкануть все спейсы.
Чистое алгоритмическое удаление сделать — это достаточно сложная задача.

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

Ладно народ. Предлагаю на этой ноте временно закруглиться.
Два основных поинта.
1. Статье на Хабре быть.
2. Возможно стоит снять и выложить на ютуб курс лекций и правильно преподнести информацию

Пускай верхний код его ловит. Что мне с ним делать внутри инсерт ? Если нью не отработал то это краш процесса

Стандартный патерн для простых структур данных — если объект не удалось построить бросать на верх эксепшин. Верхний код сам должен решать что делать. Мапа же не может на консоль выводить текст эксепшина или меседжбоксы показывать. Разве что свой ребилд вызвать и сделать более компактным дерево. Но это тупик потому что для этого все равно нужно чуток памяти еще выделить

Напишите пример кода под Линукс и Виндовс. Я добавлю

Закомитил, правда под Win,
под линукс нет доступа пока что.

Проще всего обернуть в std::unique_ptr прямо в классе. Одна проблемка — он начиная с C++11, то есть старые VS могут не потянуть (а линуксовым компиляторам до gcc5 надо явно разрешать 11-й). Так что это залог на будущее.
Ну или самому делать временный RAII-аналог в конструкторе.

С Judy Array ещё никто не сравнивал?

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

Judy медленее чуть меньше чем в два раза.

Спасибо Бро
Джуди писали крутые ребята из HP больше года целой тимой с оптимизацией под кеши проца. Это можно сказать первая серьезная попытка сделать что-то стоящее на основе Trie

Спасибо Бро
Я за справедливость. Та же Judy (1.0.5) неработоспособна и часто падает, очень сырая реализация. Кстати, на 14 Ubuntu LTS и под QNX она не проходит даже свои собственные тесты.
Джуди писали крутые ребята из HP больше года целой тимой с оптимизацией под кеши проца.
Все эти разговоры про кеши проца для домохозяек. То, что они ограничили размер структуры равным двум кешлайнам не имеет никакого критического значения. Я не увидел в коде ни одного вызова __builtin_prefetch() или _mm_prefetch() - без этого процессор работает как раньше, вычитывает больше чем надо и ни разу не может угадать куда будет обращение к следующей структуре, что гарантирует 100% cache miss.

В’ячеславе, вам дуже бракує скромності. Поводитесь та хвалите себе як вже згадані в треді російські «прінципіально новиє» школярі-шарлатани, при чому у вас причин так робити ще менше — про вас навіть сюжет на ТБ не показали, і гранд ви ніякий не виграли. Тон зневажливої до вас дискусії ви задали самі — своїми «качественно вброшу», «настоящая нетленка», «легендарный алгоритм». В кожному вашому слові зверхність та пафос. Важко вас сприймати всерйоз, інакше ніж троля чи ідіота.

Ну хорошо. Простите.
А то тут уже до уютненьного.it недалеко.

Вроде поборол сегфолт.

Компилятор GNU GCC, OS Ubuntu 10.1,
Железо вот такое rozetka.com.ua/...er_nx_g2jeu_002/p4741926

Результаты, мягко говоря, меня удивили.
Линуксовая мапа сливает в некоторых кейсах в десятки раз. (Кто там на нее надежды возлагал ?)
github.com/...ray_Benchmarks_Ubuntu.txt

Сырцы (правда опять без мейкфайла, извиняйте)
залил сюда
github.com/.../tree/master/HArray_Linux

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

Именно так. Есть алгоритмисты высочайшего уровня, которые призваны высечь в монолите выдающиеся реализации, показав тем самым настоящий Мастер Класс. А есть хомячки, такие как ты, которые бегают вокруг и скулят про копипасту, гоутушечки и лишний иострим в заголовке.

ты вообще не понимаешь кто такие «великие алгоритмисты» и как они работают.
А твои фантазии забавны, ога :)

Компилятор GNU GCC, OS Ubuntu 10.1,

ОС зверски старая. И я подозреваю, что только 32-битная, ибо:

Main.cpp:57:46: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘clock_t {aka long int}’ [-Wformat=]
  printf("Insert: %d msec, ", (finish - start));

Main.cpp:174:87: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
   printf("Insert/Search %u PERIOD keys (%u bytes each) ...\n", countKeys, sizeof(uint));

Вывести 8-байтное целое как 4-байтное работает только чудом (из-за LE плюс соглашения о вызову). Нехорошо, ибо грязь.

Про мириады comparison between signed and unsigned integer expressions я активно и не вспоминаю — тут бы с более серьёзными проблемами разобраться (хотя и эти тоже важны, там легко вляпаться в ситуацию, когда, например, −1 превращается в UINT_MAX).

HArrayInt.h:173:9: warning: ‘countValues’ may be used uninitialized in this function [-Wmaybe-uninitialized]

Заметим, это всего лишь -Wall.. Я ещё не включал -Wextra, стра-а-ашно.

Можно также заглянуть в код...


clock_t msclock()
{
    return clock() / 1000; //in ms
}

Для кого в мане писали про CLOCKS_PER_SEC? Ладно, это можно списать на желание побыстрее что-то выкатить, но всё равно плохо — непереносимостью и соответственно проблемами воспроизведения.

Линуксовая мапа сливает в некоторых кейсах в десятки раз. (Кто там на нее надежды возлагал ?)

Надежды или нет, но я предложил сравнить. А тем более, что сравнивать надо при равенстве условий. На Intʼах делать быстрое достаточно легко. А вот как, например, будет в случае, если и ключи, и значения — std::string? Разобрать какую-нибудь «Войну и мир» на строки и сделать мапу строка -> следующая строка.

А ещё std::map из GCC — это тупое red-black. Я вообще не понимаю причины любви народа к ней — AVL в среднем показывает результаты на несколько процентов быстрее, и сопровождается легче — но с нынешней дороговизной доступа к RAM надо смотреть на полноценные B-деревья. Возьмите реализацию мапы из tarantool, она на B⁺-деревьях, и сравните скорость с ней, если получится аккуратно выдрать (там иерархия кастомных менеджеров памяти). И опять же не только на целых, но как минимум(!) на строках.

правда опять без мейкфайла, извиняйте

Ну добавьте.


OBJS= HArrayVarRAM_delValueByKey.o HArrayVarRAM_getKeysAndValuesByRange.o \
        HArrayVarRAM_getValueByKey.o HArrayVarRAM_getValuesByRange.o \
        HArrayVarRAM_hasPartKey.o HArrayVarRAM_insert.o \
        HArrayVarRAM_scanKeysAndValues.o Main.o stdafx.o

CXXFLAGS= -O -pipe -Wall -Wno-write-strings -std=c++11

test: $(OBJS)
        $(CXX) -o test $(OBJS)

.cpp.o:
        $(CXX) -c $< $(CXXFLAGS)

clean:
        rm -f test ./*.o

.PHONY: clean

восстановить табы перед коммитом.

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

Надежды или нет, но я предложил сравнить. А тем более, что сравнивать надо при равенстве условий. На Intʼах делать быстрое достаточно легко. А вот как, например, будет в случае, если и ключи, и значения — std::string? Разобрать какую-нибудь «Войну и мир» на строки и сделать мапу строка -> следующая строка.

Там же есть пачка бенчмарков где в качестве ключа используется 64х байтная строка сгенерированая или рандомно или по какомуто правилу. Адаптер именно для std::string написать не представляет никакого труда и производительности он практически не просадит.

Возьмите реализацию мапы из tarantool, она на B⁺-деревьях, и сравните скорость с ней, если получится аккуратно выдрать (там иерархия кастомных менеджеров памяти). И опять же не только на целых, но как минимум(!) на строках.

Если она на B+ то это не имеет практически смысла. Может быть сканы по диапазонам ключей там будут чуток лучше в определенных контрпримерах. (Trie всетаки делает это более размашесто). Все остальное — банально больше seek times.
Ну и да, пока что мы говорим только про InMemory структуры, для которых кстате также как и для диска справедливо Latency и Seek Time.

Вот один из немногих толковых комментариев.
Ну дик він і написаний справжнім Гуру. ;)
Разобрать какую-нибудь «Войну и мир» на строки и сделать мапу строка -> следующая строка.

Это слишком просто и делал такое уже и не раз.
Я разбирал Либрусек на части. А это около 400 тысяч книг почти в 300 гигабайтах текстовых файлов. Скорость индексирования поднималась до 100 мб\сек на один поток, а размер словаря превысил 15 млн слов (книги там на разных языках).

Короче я портировали бОльшую часть под Линукс. Многие из 45 тестов работают нормально. Разница между STD::map и моей либой ощутимо выросла в мою пользу при компиляции GCC(может это конфигурация компа такая ?). Но есть в некоторых тестах хитрый сегфолт краш и мне понадобится некоторое время чтобы с этим разобраться. Особенно учитывая что после VS в этом code::blocks сидеть весьма непривычно. Но я что-то придумаю. Всем Удачи.

давай так, портируй под анроид лиункс......и там все разбирайсе — там тетпишут

Баги это просто баги. Их рано или поздно выкосят. Под Виндовс багов уже нет давно. Под Линукс, нужно еще дополнительно тестировать. На 45 нагрузочных тестах вот не падает, нормально.
А на счет ЧСВ это почемуто в украинском айти эстеблешменте он раздут до невероятных размеров. Как будто уже успели написать и протестировать по пять нетленок каждый мирового уровня. Пафос некоторых доставлят — "Портируйте мне бесплатно под Си я хочу попробовать использовать в своем личном проекте, ведь мое время дороже чем ваше !«© Или «Протестирую непременно как появится сmake файл, ведь я пользователь под Linux». Ну замечательно. Но если нормальное компьюнити, и заинтеерсовано в нормальной идее — то оно НЕ должно мне выносить мозг, а должно посильно помочь хотябы с элементарными вещами.

Мне лично, в целом всеравно. Мне больше наука дорога, алгоритм в готовом виде пылился 3 года, может быть не увидел свет вообще. Короче кому не нравится — используйте std::map и его аналоги. И веруйте что это верх инженерной мысли, гы гы.

Похоронить він її скоріше тому, що буде від всіх навколо чути «спочатку доведи до кінця, потім приходь вихвалятися». Особисто я не можу йому нічим допомогти, хоча б варто було.

Але ніхто не допоміг. Триндіти не мішки ворочати. Всі гаразди тільки надати «корисну пораду». Так ком’юніті не побудувати ніколи...

Я ж написав вище, що не можу... А так би залюбки!

Не вистачає знань саме цієї мови програмування, оточення, супутних систем та програм. Дуже давно відійшов від низькорівневого програмування, забулося майже все.

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

Але ніхто не допоміг. Триндіти не мішки ворочати. Всі гаразди тільки надати “корисну пораду”. Так ком’юніті не побудувати ніколи...
Ты правда не понимаешь? Зачем ему помогать, если он сам себе помочь не может? Он не может сделать даже элементарного — причесать код, перед тем как выкладывать. Такая внешняя небрежность свидетельствует о внутренней небрежности тоже, в продакшене я бы побоялся использовать его код. Попробовать — да, использовать — нужно разобрать алгоритм полностью, иначе врядли.

Ну так би й сказав, корона тисне..

Лучше уж корона снаружи, чем ЧСВ изнутри.

Я частенько участвую в опен-сорс. Но этому автору помогать не буду. Даже не из-за чсв, оно почти у всех опен-сорсников раздутое, а из-за личности автора. С ним невозможно будет конструктивно работать. Думаю, я не один такой.

Ну дик завжди важко співпрацювати із людиною, яку з самого початку змішали із лайном...

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

Тс облил ведром с поносом тех, кто пытались быть конструктивными

Ну не выдумывай. Свояк свояка видит из далека. Когда заходит человек чтобы дать профессиональный комментарий, а тут было не мало таких людей, то всегда почемуто разговор складывается. А когда забегает чудо вроде Прокофьева или Карякина, то имею соблазн потролить. И кстате это даже полезно, иначе будет тема с унылыми тремя комментариями.

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

Я заметил, как оно у вас сложился с Горчаком и Чидженком.

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

А когда забегает чудо вроде Прокофьева или Карякина, то имею соблазн потролить.

Их замечания имели рациональное звено, которое позже подтвердили Горчак и Чидженко (который вас, сначала, кстати, даже пытался защищать).

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

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

Ой, ну я вас умоляю. Вроде ландшафт украинского айти просто кишит первокласными продуктами мирового уровня вроде Оракла, Постгрес, Гугл и тд и тут только вот я такой никакой попался из-за которого проект «не взлетит». Даже москали со своими Яндексами, Касперскими и Бреинджеинами на две головы вше чем мы. Оглянись вокруг и схамениться, как тут уже писали. Иначе всю жизнь будете конкурировать с индусами и циганами за проекты. Ну или за границей стеснятся своих корней.

Вроде ландшафт украинского айти просто кишит первокласными продуктами мирового уровня вроде Оракла, Постгрес, Гугл и тд и тут только вот я такой никакой попался из-за которого проект «не взлетит».

В васших утвержедниях 2 логических ошибки:
1. Ваш продукт не первоклассный;
2. Ваш продукт не мирового уровня.

Ваш продукт мог бы таковым стать, есил бы сообщество решило его допилить. Однако, с вашей персоналией, шансов на то, что ваш продукт станет чего-то стоить, нет.

Оглянись вокруг и схамениться, как тут уже писали.

Зачем это мне? Чисто, чтобы потом посидеть в поносе, извергаемом вами в свободное, от работы время? Нет, спасибо. Я лучше с Ортой поработаю, он парень приятный и его продукты, как раз, мирового уровня, т.е. такие, что ими пользуется весь мир.

Ну или за границей стеснятся своих корней.

Не проецируйте свои фобии на других.

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

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

Не патриот значит.

Патриотизм не имеет ничего общего с идиотизмом. Работать с вами — идиотизм.

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

0. Есть много проектов, которые можно назвать патриотическими, а участников которых можно назвать патриотами. Ваш проект таковым назвать нельзя.
1. Ваш проект не является перспективной разработкой в силу причин, озвученных вашими оппонентами.
2. Когда проблемой является автор и идеолог, любой проект мертворожден и не стоит времени, затраченного на участие в нем.

Вот потому мне жалко всяких Физиков Теоретиков, или Математиков Теоретиков. Выкатываешь такой классную теорию, тут толпой набегают гуманитарии, маркетологи и шутники и смешуют твою теорию с навозом. В итоге только на втором, а то и третьем поколении наконец до людей наконец доходит о чем же тогда говорил автор. В Computer Science совершенно все не так. Ты делаешь чтото интересное, просто делаешь реализацию, выкатываешь benchmarks на которых компьютер полностью и недвузначно апрувит твою теорию и сразу 999 гуманитариев идут в лес. Profit !

Ты делаешь чтото интересное, просто делаешь реализацию, выкатываешь benchmarks на которых компьютер полностью и недвузначно апрувит твою теорию и сразу 999 гуманитариев идут в лес.

Ни Горчак, ни Чидженко не являются гуманитариями. Так вот, в лес вас послали они.

Выкатываешь такой классную теорию, тут толпой набегают гуманитарии, маркетологи и шутники и смешуют твою теорию с навозом.

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

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

Ни Горчак, ни Чидженко не являются гуманитариями. Так вот, в лес вас послали они.

Давай ты за них не будешь говорить. Ок ?

Давай ты за них не будешь. говорить. Ок ?

Мне не надо за них говорить. В этом треде они свое мнение сказали сами.

Вимушений зізнатися, що правда є в словах БубєнКома... Просто проаналізуйте свої слова та думки. Просто зупиніться хоча б раз та подумайте.

Дуже Дякую.
Коли почуете десь на ганку
— Ти знаешь куме СКБД Днипро в двадцять разив швидше працюе за оту москальську Монгу
— Так, звисно !
И так хитро усмихнетися, бо ви вже майже знаете чому.

Пафос некоторых доставлят — "Портируйте мне бесплатно под Си я хочу попробовать использовать в своем личном проекте, ведь мое время дороже чем ваше !«©
Ты даже читать не умеешь — 01.org/igvt-g — это далеко не личный проект.
Но если нормальное компьюнити, и заинтеерсовано в нормальной идее — то оно НЕ должно мне выносить мозг, а должно посильно помочь хотябы с элементарными вещами.
С элементарными вещами должно разбираться не нормальное коммьюнити, а нормальный автор. Написал, выложил, сделай поддержку.
Мне больше наука дорога, алгоритм в готовом виде пылился 3 года, может быть не увидел свет вообще.
В общем ещё один мастер «и так сойдёт», не способный довести идею до конца, бросающий проект на 80% готовности. Таких в мире миллионы и цена им 0.

Можете допомогти довести до 100%... Якщо ви дійсно маєте унікальний досвід як це зробити.

Какой в этом смысл? В мире есть множество увлекательных вещей, куда подтирание жопы за малолеткой не входит.

Забув як сам був таким?

Никогда не был таким, может поэтому мне такие индивидуумы чужды.

Ви одразу народилися із знаннями да досвідом?

У меня до сих пор нет ни знаний, ни опыта.

Топик стартер не имеет ни знания, ни опыта. При этом же считает себя ылитой, а окружающих — быдлом. Не желает учиться. Агрессивен.
Людей с таким маиндсетом совсем немного в девелопменте.

А я с этим категорически не согласен. Узкая ориентированность только на свой результат, создание условий под него и отмахивание от всего, что видится прямо сейчас не нужным — это, да, присутствует. Типаж «испанский лётчик» — смотрит только вперёд в узкую амбразуру туда, куда летит, не обращая внимания ни на что вокруг. Но я видел несколько раз, как такие добивались таки своего прорывного результата — а ту же самую вычистку/адаптацию делали уже другие, не столь концентрированные, с широким опытом. Даже завидую — меня ничто пока так активно и глубоко не интересовало.

А результат таки есть — по крайней мере бенчмарки показывает очень вкусные цифры, и valgrind молчит. :)

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

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

ну да, мы откровенных дибилов фильтруем. Я так понимаю, потратить 15 минут в день на отчетность — для тебя непосильный труд? :)

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

Ніхто не любить критику. Ви себе зараз чуєте? Ви зараз дуже активно підтверджуєте, що автор топіка правий на 200%.

Ну да, чо уж там, нормальное поведение: вывалить полурабочий говнокод в паблик, обьявив себя гением.
наехать на несогласных, заявив что все они «не шарят».
когда ему потыкали в конкретные косяки — заявить что «а пофик правьте сами».
когда поправили — сказать «криво поправили», вы все не шарите.
... и после этого обидеться на весь мир, заявив что его не уважают и не хотят помочь.
Все это с пафосом и чсв овер 9000.
Нормальное поведение, что уж там. Правильное.

Ви заздрите, чи що? Мене, як спеціаліста в певній галузі, абсолютно не турбує максималізм та манерність автора топіку. Тому що я навчився дивитися в сутність, а не звертати увагу на мішуру. Ситуація така, що реальний клас показав тільки Нетч. Але це й зрозуміло.

Тому що я навчився дивитися в сутність, а не звертати увагу на мішуру.
Да нету там сути. Только мишура и пафос.
Я это по косвенным признаком в другом топике еще понял. Ни разу не качая и не компилируя сорцы.
И завидовать тут совершенно нечему.

Когда умный показывает на небо дурак смотрит на палец ©

Вера, которую не разделяет никто, называется шизофренией. ©

Покажіть свій проект, яким би ви могли пишатися. Покажіть свій клас. А то тут більшість розмов схожі на діалог:
— Все лайно! Всі лайнюки! Я — краще!
— Чим?
— Чим лайно!
:-/

Сперва добейся, ага.
Я даже на NDA не буду ссылаться, хотя все проекты в которых я участвую — под ним. И даже на то, что продакшн кода я сейчас пишу достаточно мало.
Просто нет такого проекта, который я сделал сам. Все в коллективе. Кто-то проектирует, кто-то педалит, кто то ревьюит...
поэтому попросту нет «моего» кода и «моего» проекта.
А любая ссылка на коллективное творчество ожидаемо приведет к бурлению на тему «а ты же не сам это сделал!»

Сперва добейся, ага.
Мимо каси.
Клас програміста заключається не в тому, що він як мавпа може кидатися лайном, а в тому, що він може а) розібратися в коді іншого програміста (навіть якщо він здається лайнокодом) б) самостійно виправити помилки або написати власну реалізацію й доказати свою точку зору кодом. Це як було із Еплом та Майкрософтом. Майкрософт міг скільки завгодно стібати Епл з приводу телефонів, але так і не спромігся зробити щось краще. Ітог ми знаємо: Епл на коні, Майкрософт злився з ринку. Так само і тут. Всі відмазки про те, що розбиратися в коді ТС або допомагати йому, є зайвою витратою часу, я особисто сприймаю як повну професійну імпотенцію критиків. І всі намагання щось доказати без надання коду тільки підтверджують мою правоту. На конкурс мірянія піпіськами не приходять із власними лінійками ;)

В общим да, «сперва добейся и напиши ему код». Лично я считаю такую точку зрения крайне недалекой и ущербной.
Что только подтверждается этим топиком: некоторые собеседники таки скачали, скомпилили, запустли, указали на ошибки и предложили чендж реквесты на заливку. В ответ получили от топикстартера очередную порцию говна, что «вы тут багов понаделали», причем без конкретики.
Повторюсь, я же чисто по формальным признакам, потратив буквально минут 15, определил что «не взлетит» — это, не побоюсь этого слова, признак класса :)

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

dou.ua/...rums/topic/18849/#1005075
Вот эта ветка показательна, ога.

В общим да, «сперва добейся и напиши ему код». Лично я считаю такую точку зрения крайне недалекой и ущербной.
Таке враження, що ви не читали мого поста. Не треба добиватися, треба просто не вважати себе самим розумним. А якщо заявив про себе, то будь ласка доказуй свій клас, інакше ти простий балабол. Давайте простими словами. Змагання виграють не ті, хто краще за все розповідають, які вони круті та як вони всіх можуть виграти, а ті, хто стають на стартову лінію та реально доказують, що вони щось можуть. Це не «спочатку досягни», це інше.
Ви не допускаєте того, що люди могли не розібратися із задачею та реалізацією? Ні? Зовсім? А я таке бачу крізь. Мені особисто багато разів казали — не полетить. А воно, блін, летіло. Ще й як летіло!
А якщо заявив про себе, то будь ласка доказуй свій клас, інакше ти простий балабол.
Еще раз по буквам: народ полез разбираться с кодом и тыкнул автора в конкретные проблемы.
Я же указал на то, что код говно, посмотрев на него по диагонали. То есть мой прогноз оказался верен и правилен. Вот собственно говоря в чем и класс. :)

Ваша суб’єктивна оцінка коду не цікава нікому. Ви це розумієте?

Ваша суб’єктивна оцінка коду не цікава нікому. Ви це розумієте?

Она объективна. Это подтвердили девы с proven record, как Горчак и Чидженко. Другое дело, что говнокод мог бы кто-то из сообщества, подправить. Там рефакторинга на 1-2 вечера + неделька на портабельность нормальную + покрытие тестами еще на пару неделек.

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

З.Ы. Скажите, а вы не мульт Бубна?

Слушай, ну кто хотел комититить и у кого были идеи к улучшению, свой код уже считай закомитил.
А кто оправдывается, ну тот оправдывается.
Не понимаю о чем спор.

«...а вы и есть за меня будете?»

Слушай, ну кто хотел комититить и у кого были идеи к улучшению, свой код уже считай закомитил.

github.com/Bazist/HArray.WIN
github.com/Bazist/HArray.UNX

0 форков, 0 пулл реквестов. Никто не хотел комитить, как я погляжу.

Не понимаю о чем спор.

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

Ну во-первых мы не на сайте знакомств чтобы обращать внимание на персоналии, а в вполне себе формальных координатах кода и алгоритмов.
Во-вторых, если вы не хотите комитить, то какие ко мне вообще претензии что я не учел чьи-то хотелки ?

Ну во-первых мы не на сайте знакомств чтобы обращать внимание на персоналии, а в вполне себе формальных координатах кода и алгоритмов.

Как я уже говорил, ваше утверждение неверно. Либо дев все делает сам, либо ведет себя по-челвоечески и ему помогают. Вы не хотите делать сами и все делаете дял того, чтобы вам не помогали.

Во-вторых, если вы не хотите комитить, то какие ко мне вообще претензии что я не учел чьи-то хотелки ?

А зачем что-то коммитить, чтобы потом слушать о том, что мы все поломали и у вас все классно? Автор любого PoC либо взаимодействует с сообществом и оно улучшает его код, либо получает реакцию, как в сиим топике.

Она объективна.
Можете називати як завгодно. Практичної цінності для оточуючих вона все одне не несе ніякої.
Но, лично я этого делать не стану, особенно, после писков аффтара о том, как ему код сломали, да какие все идиоты вокруг.
Автор гарно спілкується із іншими учасниками топіку. Окрім того ще й нормально сприймає їх зауваження та пропозиції. Задайтеся запитанням, чому?
З.Ы. Скажите, а вы не мульт Бубна?
Я його навіть особисто не знаю.
Можете називати як завгодно. Практичної цінності для оточуючих вона все одне не несе ніякої.
Не несет тем, кто не желает слушать критику. А я тут не работаю, а развлекаюсь, соответсвенно не парюсь по поводу того что меня не услышали и не подбираю политкорректных формулировок.
Пару примеров.
Я отметил что в HArrayVarRAM::insert есть проблемы. Посмотрев ее по диагонале. Потом народ нашел проблемы.
Я отметил: проблемы с CI/CD. Аффтар потом начал: “поффиксал в версии под линух, но не под виндовз...”, “кто-то что-то поломал, бывает...”
Было бы желание услышать...

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

Можете називати як завгодно. Практичної цінності для оточуючих вона все одне не несе ніякої.

Ваше утверждение не соответствует действительности.

Автор гарно спілкується із іншими учасниками топіку. Окрім того ще й нормально сприймає їх зауваження та пропозиції. Задайтеся запитанням, чому?

Не увидел нормального общения и восприятия замечаний. Поэтому, мне не надо задаваться вопросом, т.к. он противоречит фактам.

Комменты от изначально адекватных участников, которые сильны конкретно в той специализации, на которую претендует аффтар:
dou.ua/...ign=reply-comment#1006214
dou.ua/...ign=reply-comment#1005983
dou.ua/...ign=reply-comment#1006079

А вот просто несколько примеров замечаний и смотрим на реакцию ТС:
dou.ua/...ign=reply-comment#1006077
dou.ua/...ign=reply-comment#1005368
dou.ua/...ign=reply-comment#1005025
dou.ua/...ign=reply-comment#1005130
dou.ua/...ign=reply-comment#1005014
dou.ua/...ign=reply-comment#1005008
dou.ua/...ign=reply-comment#1004977
dou.ua/...ign=reply-comment#1004906
dou.ua/...ign=reply-comment#1004901

Ну и вся ветка: dou.ua/...ign=reply-comment#1005075

которые сильны конкретно в той специализации, на которую претендует аффтар:

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

А вот просто несколько примеров замечаний и смотрим на реакцию ТС:

Ну, после фантастики типа «на ARM thumb, int — 16 бит» я бы просто в игнор занёс.
Реально, замечания (включая мои) до сих пор были только придиркой к форме. Я не видел пока ни одного по сути алгоритма, от слова «вообще».

Ну вот есть мнение, что если автор типа алгоритмист, но ниразу не программист — то может ему и алгоритм надо презентовать в виде блок схемы на форуме математиков?

Почему-то :) почти ни в одной книге по алгоритмам так не делают.
Хотя есть любимый Кожаевым «Дракон»... ой, простите, одраконахнислова

смею предположить, что исключительно по той причине, что квалификация авторов этих книг позволяет им написать вменяемые примеры алгоритмов на каком нибудь языке прораммирования.
Впрочем мат статьи с квадратиками и ромбиками, я видел в приличном количестве — то есть это ниразу не исключение.
Ну и я вот рандомно открыл Кнута — пожалуйста и картинки и примерчик реализации:
joxi.ru/nAyXvP5uXoVg32

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

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

Я наверное больше доверяю мат статистике. И конечно допускаю вероятность того, что автор:
— будучи неадекватен в девелопменте, куммуникации с людьми, теоретических основах распределенных СУБД(был у нас с ним в другом топике спор на эту тему), коммерческой разработке и внедрении софта
— декларируя себя как супергуру во всех озвученных областях,
является гением от алгоритмизации. Но считаю такую вероятность околонулевой.

Ну, после фантастики типа «на ARM thumb, int — 16 бит» я бы просто в игнор занёс.

Перепутал чел размер инструкций и размер данных. Бывает. Однако, таки по стандарту зацикливаться на конкретную битность не стоит типов, если не используются *_t типы, не так ли? Есил мы говорим о портабельности, то таки надо либо жестко зашивать типы через *_t, либо уже писать с рассчетом, что тип не обязательно именно битности х86_64. На тех же avr, насколько я помню, int — таки 2 байта.

Я не видел пока ни одного по сути алгоритма, от слова «вообще».

Простите, но вопрос алгоритма не стоит лично для меня, пока такая форма. Лично у меня возникает ощущение, что алгоритм лазает по памяти, которую он не аллочил. Чтобы точно понять и дать фидбек, мне надо отрефакторить код. Чтобы отрефакторить код мне понадобится инпут от ТСа. А инпут от него будет: «раз у вас тормозит, значит вы напортачили, идиоты»

На тех же avr, насколько я помню, int — таки 2 байта.

Ну вот потому я говорил — что смотрю только на те платформы, где такой толстый-и-быстрый trie имеет хоть какой-то смысл. Там, как правило, данных не менее единиц гигабайт.

Простите, но вопрос алгоритма не стоит лично для меня, пока такая форма.

Ну вот очень жаль, потому что именно такая позиция дала 90% мусора данной темы.

А инпут от него будет: «раз у вас тормозит, значит вы напортачили, идиоты»

Вот потому я подождал того, что таки скомпилировалось (даже с учётом, что makefile к нему я породил сам), и там напустил тест, и ещё и константы покрутил для проверки поведения. И числа пока что в этом показывают именно анонсированную тенденцию.
А если бы было наоборот — то не было бы повода пожаловаться на «сами напортачили».

Ну вот потому я говорил — что смотрю только на те платформы, где такой толстый-и-быстрый trie имеет хоть какой-то смысл.

Да как сказать. Автор указывает на быстрый инсерт. Скажем, на массивах с непредсказуемым размером это бы тоже могло помочь. Или на sparse массивах. Или банальная автоподсказка, еще б туда расстояние Левенштейна вставить.

Ну вот очень жаль, потому что именно такая позиция дала 90% мусора данной темы.

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

еще б туда расстояние Левенштейна вставить.
Было у него попытка «супер поиска» на ixbt. Там он всем рассказал что расстояние Левенштейна — это фигня и прошлый век, у него гораздо круче будет.

Вся ваша доктрина держалась на некотором мембере, который умудрился внести как минимум 5 правок в мой код за 5 минут, наломать кучу дров и непременно меня обвинить что код нерабочий и тесты сфальсифицированы. И вы это считаете нормальным. А теперь я хочу вас спросить, какая вообще разработка может взлететь в такой среде. Если вам показываешь уже готовый рабочий код, просто используй, просто померяй, секономолено 5 лет. А ведь еще есть Днипра в которой немало охеренных идей реализовано. И в ней действительно немало багов. Если вы выносите так мозг с мапой где прикопаться можно только откровенным мошенничеством, то что произойдет с целым документоориентированым сервером данных. Боюсь что он точно среди хомячков не взлетит. Ну пусть хоть мапа будет.

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

Это не так: dou.ua/...ign=reply-comment#1006880

Да, после этого вы снизили накал дискуссии, но недостаточно.

И вы это считаете нормальным.

Ваш код, действительно, не готов для продакшн. Но это нормально для PoC. А вот ваша реакция вида «вы все идиоты и не лечитесь, один я в белом пальто стою красивый» — это ненормально.

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

В том-то и звгвоздка, он не совсем рабочий. Он рабочий на уровне PoC. Рабочий библиотечный код не может быть грязным с огромным количеством дублирования, с отсутствующими smart pointers и exception handling. Более того, он не может лазать по памяти, которую вы не заалочили.

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

Я ничего не выношу, я отмечаю как участник open source community, что никто не будет заниматься взомжоно и гениальной (я не признаю пока гениальности, не подумайте), с персоналией, имеющей ваши подходы.

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

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

В том-то и звгвоздка, он не совсем рабочий.
Ну сейчас тебе расскажут про фундаментальный код который один раз пишется и потом никогда не читается и нидайбожы не правится. В общим очередная итерация: dou.ua/...ign=reply-comment#1004913

Слушай. Я не раз общался с архитекторами в компаниях мечты и это люди совершенно другого уровня мышления. Я не понимаю кто тебя архитектором в ипам сделал. Это чудовищный прое эйчаров. Максимум мидл и я совершенно не шучу. Нет никакого фундаментального мышления. Это даже редкость среди синиоров не то что для архитекторов.

таое мнение чрезвычайно ценно для епама вообще и для иеня в частности :)))

Не треба добиватися, треба просто не вважати себе самим розумним. А якщо заявив про себе, то будь ласка доказуй свій клас, інакше ти простий балабол. Давайте простими словами. Змагання виграють не ті, хто краще за все розповідають, які вони круті та як вони всіх можуть виграти, а ті, хто стають на стартову лінію та реально доказують, що вони щось можуть.

Интересно, почему вы к ТСу с его необоснованной претенциозностью такие замечания не пишете? Класс не доказал, побалаболил трохы и усе.

Я вже писав, що не володію матчастиною. Допоміг би залюбки. Не через його пафос чи ПВВ, а тому що гарні починання завжди треба підтримувати. Адекватне суспільство можно побудувати тільки на альтруїстичному обміні інформацією та досвідом. Стековерфлоу тому дуже яскравий приклад. Називати код іншої людини лайном — це шлях нарцисизма, меншовартості та самозатвердження за рахунок інших. Коротше, бажаю швидше вам вилікуватися від дитячих комплексів.

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

Адекватність — суб’єктивна міра сприйняття. Лайнокод — теж. Кидатися лайном — мавпляча забаванка. Такий собі комплекс неповноцінності. Ви можете скільки завгодно пишатися тим, що назвали лайно лайном, але для інших ви не правдоруб, ні.

Отчего же. У психиатров есть целый набор критериев, по которым они делают вывод — адекватный человек для общества или нет.
Равно как и в софтостроении есть целый набор критериев, относительно которых делается вывод — адекватный код или не очень.
Если вы не в курсе про это, то это исключительно ваши проблемы.

А хто сказав, що автор критеріїв був адекватним? :D

опыт использования предложенных критериев. И опыт неиспользования предложенных критериев. Про софт управления железом тойоты читали? Вот так там последовательное неприменение best practice. И наверняка там тоже много разговоров про фундаментальный код, в котором все в угоду перфоманса и про то что написанный код решает задачу — и это главное.

Перечитайте мой вопос выше. Почему вы не адресуете претензии, озвученные вами, ТС?

Адекватне суспільство можно побудувати тільки на альтруїстичному обміні інформацією та досвідом.

Именно, для этого надо, чтобы вокруг автора идеи появилось комьюнити. В случае ТС это невозможно в виду причин, озвученных мною неоднократно.

Коротше, бажаю швидше вам вилікуватися від дитячих комплексів.

Опять же, адресуйте это замечание ТС.

Називати код іншої людини лайном — це шлях нарцисизма, меншовартості та самозатвердження за рахунок інших.

Простите, но изначально с автором так не говорили. Автор сам направил дискуссию в это русло своими реакциями. Пруф тут: dou.ua/...ign=reply-comment#1006880

Почему вы не адресуете претензии, озвученные вами, ТС?
Тому що він мусить пройти весь шлях розвитку особистості самостійно. Неадекватна критика та кидання лайном не стимулює в нього адекватної реакції, а тільки визиває агресію та зворотнє кидання лайном.
Простите, но изначально с автором так не говорили.
Що ми читаємо в постах від коментаторів?
В общем ещё один мастер “и так сойдёт”, не способный довести идею до конца, бросающий проект на 80% готовности. Таких в мире миллионы и цена им 0.
Зверхнє ставлення до ТС
Если бы были мозги, то ты бы это сделал в первую очередь.
Приниження
безумное ЧСВ и не желание слушать других. Поиграется еще некоторое время, потом закинет в пыльный чулан, ибо надоест.
Офігенно “констуктивна” критика! Приниженя можливостей ТС

Давайте продовжу

Какой в этом смысл? В мире есть множество увлекательных вещей, куда подтирание жопы за малолеткой не входит.
а все потому что про CI ты ваще ни в дуб ногой:
Далі продовжувати? Чи вже зрозуміло й так хто чого вартий?
Неадекватна критика та кидання лайном не стимулює в нього адекватної реакції, а тільки визиває агресію та зворотнє кидання лайном.

Я вам кинул ссылки на нормальные комменты. Автор сам спровоцировал агрессию.

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

Що ми читаємо в постах від коментаторів?
Зверхнє ставлення до ТС
Есил вы потрудитесь, то этот коммент Горчака пришле уже после того, как Горчак предложил причесать код, чтобы он мог его показать в QNX и Intel ( dou.ua/...ign=reply-comment#1005965 ) . На что, кстати, Горчак получил весьма некорректный ответ. Странно, чего это после такого ответа специалист с proven record вдруг начал смотреть свысока.
Далі продовжувати? Чи вже зрозуміло й так хто чого вартий?

Вам бы перечитать всю дискуссию с каждым из оппонентов. И всюду будет, как с Горчаком. Люди пытались выйти на конструктив, их ТС назвал идиотами.

Зверхнє ставлення до ТС
Что вижу, то и говорю, типаж ТС довольно распространен.
Приниження
Я просто был удивлён таким гордым отказом. Кто-то оторвался от реальности и не понимает то, чем дышит мир коммерческой разработки. Как мотивировать себя что-то делать? Да никак — оставайтесь в жопе ©.
Что вижу, то и говорю, типаж ТС довольно распространен
Як і ваш. На жаль...
Кто-то оторвался от реальности и не понимает то, чем дышит мир коммерческой разработки.
Як можна відірватися від того, де жодного разу не був?
Як і ваш. На жаль...
А ты, а ты ... детский сад.
Як можна відірватися від того, де жодного разу не був?
ТС пишет посты не приходя в сознание? Вау!
А ты, а ты ... детский сад.
Розумію, нікому не приємно чути правду...
ТС пишет посты не приходя в сознание? Вау!
Навіщо продовжувати образи людини? Ви так самозатверджуєтеся?

Ты хоть читаешь то, что пишешь?

Ты даже читать не умеешь — 01.org/igvt-g — это далеко не личный проект.

И что там ?

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

В эту поддержку не входит бездумное переписывание на все возможные языки.

В общем ещё один мастер «и так сойдёт», не способный довести идею до конца, бросающий проект на 80% готовности. Таких в мире миллионы и цена им 0

Не неси чушь. Ты же не идиот и понимаешь, что Ассоциативный Массив достаточно простая сруктура и в плане интерфейса и в плане кода. Ее дописать, протестировать, покрыть тестами несчастные три функции в интервейсе и выловить все баги достаточно не сложно. Что я и сделал.

Ее дописать, протестировать, покрыть тестами несчастные три функции в интервейсе и выловить все баги достаточно не сложно. Что я и сделал.

TestCase 1:

# uname -a
QNX localhost 7.0.0 2016/10/05-09:59:37EDT x86pc x86_64
# ./ha64
=== HArrayInt VS std::map<int,int> testing ===
Insert/Search 1000000 SEQUENCE keys (4 bytes each) ...

run fault pid 479260 tid 1 signal 11 code 1 ip 0×804e003 ./ha64
pid 479260 core file created at /root/ha64.core

TestCase 2:

# uname -a
QNX localhost 7.0.0 2016/10/05-09:59:12EDT x86pc x86
# ./ha32
=== HArrayInt VS std::map<int,int> testing ===
Insert/Search 1000000 SEQUENCE keys (4 bytes each) ...

run fault pid 454684 tid 1 signal 11 code 1 ip 0×804dd96 ./ha32
pid 454684 core file created at /root/ha32.core

Вопросов больше не имею. 2 фейла из двух. Разница с линуксом — совсем не прощаются выходы за пределы аллоцированной памяти, чуть что, процесс сразу снимается, ибонефиг.

Что тут меряли, код в студию.
Эта HArrayInt вообще простая как табуретка.

Что тут меряли, код в студию.
Без единого изменения вот этот код:
github.com/.../tree/master/HArray_Linux

Хорошо. Почему у других не падает. У тебя падает. Конфигурация ? Компилятор ?

Другая операционка, я же написал в исходном письме.

Что-то ты юлишь. Какая такая другая операционка. На которой гоуту и офсеты не работают ? Вчера тестировал под Убунту и все ок. Там вообще есть специфика, если ты вставляешь под 40 млн рандом ключей и у тебя есть хотя бы один микроскопический баг, он обязательно выстрелит тебе рикошетом в голову.

Вообщем проверь что Int 32 бита, char 8 бит и short 16 бит на твоей кофемолке, если все так то должно работать !
Или заведи ишью на гите, я попробую глянуть что там может быть.

ообщем проверь что Int 32 бита, char 8 бит и short 16 бит на твоей кофемолке
Чувак, на дворе 21 век. В сад, в общем....

А кстати — размеры на твоей QNX именно такие? (без связи с обсуждаемым софтом)

Unix LP64.

OK, это хорошо. Просто 21-й век, увы, разнообразнее. С одной стороны винда с sizeof(long)==4, с другой Swift с, цитирую, «On a 64-bit platform, Int is the same size as Int64».

В >99.99% реальных платформ масштаба «не самый крошечный embedded» (то есть те, для которых данная разработка вообще хоть как-то может пригодиться) именно такие размеры — это включает в себя x86 всех видов, ARM, MIPS, Power, другие, которые встречаются.
Поэтому реальность такова, что на это таки можно залагаться (если именно C/C++), и будет такой ещё много-много лет.
Мало того, я у себя на практике вынужден точно так же залагаться на sizeof(long)==8 и на LE. Для этого, конечно, есть специальный unit test, который первым сломается при попытке перенести на что-то более странное (например, Windows, или из железа — zSeries), но сейчас не ориентируемся на другое.

Выше уже писали, что была ошибка из-за неверного размера.

Не видел. Видел ошибку типа «выделили массив размера 1, используем N», но это никак не вопрос размера базового типа.

Есть типы данных int_32, int_fast32 и т.п.

Я как раз _не_ говорю, что не надо использовать эти типы и жёстко оставаться на int/etc. Если бы я адаптировал этот код под свои правила, я бы перевёл на типы из stdint.h или поискал вариант генерализации.
Но основной посыл всё-таки в том, что это — в краткосрочной перспективе — не имеет никакого отношения к основному обсуждаемому вопросу, и завязка на фиксированные размеры достаточна, чтобы взлететь.

Никто пока не заглянул всерьёз внутрь алгоритма, все обсуждают выделение памяти, обработку исключений, размеры типов и даже форматирование кода — вопросы, никак не связанные с алгоритмом. Что показательно.

Потому что автор кода — не автор алгоритма.

Ну если верить сравнению с trie от HP, то тут алгоритм таки новый.

Зачем копать глубже, если уже на поверхности видна куча багов и проблем?

Именно затем, что не в них основной вопрос.

Ну напиши лучше реализацию. Только чтобы результаты были не хуже чем здесь
github.com/...ray_Benchmarks_Ubuntu.txt

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

Чем плох код ? Тем что он работает быстрее чем 99% других поделок ?

Ну так делайте, не вижу реквестов в гите.
У меня живого коммерческого опыта на Си всего 6 месяцев. Я чистый дотнетчик под вин и совершенно мне не интересно заниматься прилизованием тем более под линукс. Вот баги да — я готов пофиксить. Ибо это сам алгоритм. Но заниматься тупым прилизованием не вижу смысла

куча багов и проблем

Наша песня хороша, начинай сначала ©

не надо использовать эти типы и жёстко оставаться на int/etc

Не совсем понял почему поднялась буча по поводу 32х битного инта. Предусматривая это, я специально в stdafx.h ввел алиасы uint, ulong, uchar и дальше везде их использую. Чтобы специально отвязаться от конкретных платформ. Соотвественно можно попробовать запустить и на системах где int и 16 бит

#define uint unsigned __int32
#define ulong unsigned __int64
#define ushort unsigned __int16
#define uchar unsigned __int8
#define ucode unsigned __int8

ты первый додумался до такого новаторского, 7е побо/юсь этого слова — прорывного шага в С++ девелопменте! А вот эти вот чуваки — они не шарят, так фигню всякую лепят... www.boost.org/...ndian/doc/arithmetic.html

а ты бы посмотрел сколько там нюансов, про которые ты и не задумывался. В такой то мелочи. Что же говорить о «ручном» распределении памяти?

Есть така старая байка, проект Манхеттен и создание ядерной бомбы. Американцы создали свою бомбу, но проблема была в том что она была размерами с многоэтажный дом и они не знали как это решить. У Сталина были свои люди там и советы параллельно вели работы по создании того же самого. Но он специально сказал чтобы тем инженерам которые работали в советах над бомбой не показывали чертежи американцев. И советские ученные создали «слойку», достаточно компактную чтобы ее можно было доставить самолетом.

Пересказал возможно с неточностями но мораль думаю понятна. Не всегда полезно просто «передирать» точь в точь чужие решения. Иногда лучше пройти по своему пути и получить качественно другое решение.

Прекрасный пример, очень хорошо иллюстрирует как твой образ мышления(костыли и велосипеды, ога), так и твой полный отрыв от реальности и незнакомство с историей.
Потому что в нашем мире, американцы создали компактную ядерную бомбу, что дважды и доказали в японии.
А русские свою первую версию передрали один в один. С запозданием в пару-тройку лет. И успели сделать так быстро исключительно потому что повторяли как обезьяны технические решения американцев, не тратя тысячи человеколет на резеч с неизбежными тупиковыми ветками стоимостью в сотни человеколет. За подробностями — к судоплатову.

Ох уж эти двоишники. Им бы лишбы бездумно «передирать» тупиковые решения с соседней тетрадки троишников.
Цитата с вики.
«В 1948 году А. Д. Сахаровым были выдвинуты, на основе расчетов, основополагающие идеи конструкции водородной бомбы РДС-6[5]. После этого разработка бомбы пошла по двум направлениям: „слойка“ (РДС-6с), которая подразумевала атомный заряд, окруженный несколькими слоями легких и тяжелых элементов, и „труба“ (РДС-6т), в которой плутониевая бомба погружалась в жидкий дейтерий. США разрабатывали похожие схемы. Например, схема „Alarm clock“, которая была выдвинута Эдвардом Теллером, являлась аналогом „сахаровской“ слойки, но она никогда не была реализована на практике. А вот схема „Труба“, над которой так долго работали ученые, оказалась тупиковой идеей.»

о хосподи, ну как же так. Иди почитай что разрабатывалось в рамках проекта манхеттан, на который ты ссылаешься, и чем ядерное оружие отличается от термоядерного.
А то у тебя получается классическое — «чула дзвон, но нэ чула дэ вон».

#define uint unsigned __int32

Проблема в том, что видя «uint» 99.9% читателей будет подозревать всего лишь сокращение для локального unsigned int. Соответственно разрядности последнего. Потому что это ну очень уж типичная манера. А после этого, если, например, int 16 бит, а твой uint 32 — это грубейшее нарушение POLA.

Если ты бы назвал их BoobenInt и т.п. — вопросов бы, скорее всего, не было.

Мало того, я у себя на практике вынужден точно так же залагаться на sizeof(long)==8
Я последние 15 лет полагаюсь только на stdint. И мне откровенно начхать на размер лонга.

Кое-где не я устанавливаю правила. Написано long — значит, так и будет. Потому что всякие интерфейсы к хери, например, написанной железячниками, которым какой-то злой гипнотизёр внушил, что они классные программисты.

Хм, надо будет сделать очередную попытку запинать народ на тему новых типов :)

Хм, надо будет сделать очередную попытку запинать народ на тему новых типов :)
Обычно после открытия stdint начинается вторая стадия болезни — stdint пихается везде, где нужны машинные типы и не нужны. Что-то типа такого: int32_t status=ioctl(....);

Потом обычно наступает ремиссия и просветление.

int32_t status=ioctl(....);

Ну у нас до такого не дойдёт, я уверен. Этим больше суровые виндовозники страдают, распихивая везде DWORD и тому подобное :)

Вот ты оценишь — работаю сейчас с одной либой с API вида

int foo_send (foo_channel_t ch, const void ∗buf, size_t payload_len, int ∗nsent)

возвращает статус (0 или errno), объём отправленного — в *nsent. То есть передавать я формально могу столько данных, сколько влезает в size_t (считаем, 2**64-1), а вот получить подтверждение переданного я могу только на 2**31-1 из них :)
Уж лучше бы они везде int32_t честно воткнули, не страдая попытками впихнуть невпихуемое...

Разница с линуксом — совсем не прощаются выходы за пределы аллоцированной памяти, чуть что, процесс сразу снимается, ибонефиг.

Хм, я напустил valgrind с увеличенной redzone-size, и тот молчит. Это что же за «вызход за пределы аллоцированной памяти» такой у тебя?

Что такое memory overcommit ты знаешь, просто на него надо обращать внимание, ибо иногда за это отрывают руки.

==23708== Warning: set address range perms: large range [0×84ca040, 0×284ca048) (undefined)
HArrayInt => Insert: 500 msec, Search: 202 msec.
==23708== Warning: set address range perms: large range [0×84ca028, 0×284ca060) (noaccess)

За 5 тестовых итераций статистика по page fault:

mike@mikenfs:~$ ps -o min_flt,maj_flt 23833
MINFL MAJFL
302304 0

Под конец тестирования:

root@mikenfs:~# ps -o min_flt,maj_flt 23833
MINFL MAJFL
1762243 0

Что он там меряет одному планктону известо. Использование calloc() вместо new/malloc ускоряет работу на 40% — это же писец.

P.S. Всё, я пошёл мыть руки после такого кода...

Что он там меряет одному планктону известо.ц.

Что мы меряем — известно. А вот что ты меряешь, пока что — неизвестно.
Кстате ты std::map померял хоть по своим параметрам, или это игра в одни ворота ? Судя потому как она проц грузит там трешак похуже будет.


Использование calloc() вместо new/malloc ускоряет работу на 40% — это же писец.

Уже писал. Там по сути свой рукопашный аллокатор памяти. Операция new вызывается в достаточно редких случая, только когда нужно запросить новую страницу. Поэтому твой calloc почти ниначто не влияет.
Но как нибудь можно прикрутить на следующем этапе оптимизаций.

Там по сути свой рукопашный аллокатор памяти.
Прикрути правильный аллокатор к std::map — будет тебе такая же скорость, что и требовалось доказать. В QNX очень быстрые пул-аллокаторы, только ты ж память жрёшь по пол гига на массив, тоже касается и стека, что работает в винде и линуксе только благодаря lazy memory mapping.

Какие пол гига ? Зависит от того сколько ты собираешся вставить ключей и какое ты передаешь свое capacity в функцию Init. Передай меньше чем 26, например 20 и будет тебе счастье.
Практика показывает, что моя сруктура даже экономней потребляет память чем std::map и GLib
wiki.pikosec.com/...tle=HArrayInt_VS_std::map
wiki.pikosec.com/...ayInt_VS_GLib::GHashTable

Прикрути правильный аллокатор к std::map

Надежды вьюношей питают. Прикручивай, но помни, что это соревнование не между RB Tree и RB Tree, здесь, какбы тебе сказать, немного мат фундамент другой. А ты его не понимаешь, от слова совсем. Потому никакой аллокатор не спасет.

Прикрути правильный аллокатор к std::map — будет тебе такая же скорость, что и требовалось доказать.

А вот это интересно. Есть быстрый рецепт, как это сделать? А то я с кастомными аллокаторами пока на очень далёкое «вы».

Хорошая статья (их много вообще) devnikhilk.blogspot.ca/...sample-stl-allocator.html

Ладно, я понял, что вопрос неверно задал, но, скорее всего, на него ответа вообще не было бы. Я хотел спросить про средство, которое бы «завернуло» эффективным образом мелкие аллокации в крупные, но потом понял, что тут таки надо алгоритм менять. Так что забей.

Аллокатор существует для «протачивания» std::map под определенные условия задачи. Если вы это сделаете, то скорей всего перекосите мапу для решения определенной задачи. Вообщем, если бы было все так просто, наверняка Степанов (идеолог STL) и сотоварищи встроили бы такой аллокатор изначально.

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

Итак:

= Бинарные деревья (B-Tree, RB-Tree, AVL, KD, Splay и др.)
В общей сложности сложность поиска Log(N). Причем это распросраняется фактически на весь датасет ключей, тоесть это средняя температура по больнице.

= Хештаблицы.
Best Case пишут что O(1) но на самом деле трохи брешут. Потому что скорей О(|K|) ибо ключик таки нужно еще пропустить через хешфункцию, которая может содержать тяжелые матпреобразования, а когда пришли по адрессу, то еще перепроверить что мы правильно пришли.
Worst Case (который случается достаточно редко но с обьемами данных растет) это в общем случае без оптимизаций будет равен O(N).

= VyMa\Trie.
Best Case честное О(1) без каких либо. И этот лучший случай работает условно в 9 из 10 случаев.
Worst Case который случается только если «бомбардировать» структуру последовательными ключами, на самом деле вырождается в строго фиксированые 10 seek times и не более. Тоесть в общем случае это O(|10|). Вы это гарантировано получите, если ваш сет будет содержать больше 4 миллиардов ключей. Теоретически можно было бы избавиться и от вот этого O(|10|), имплементировав вариатор размеров блоков, но эта задача сейчас сложная даже для меня. Просто держим в уме — что это возможно. Еще интересно что Worst Case может быть и выше O(|10|) поскольку он зависит также от длины ключа. Если вы работаете с ключами выше 40 байт, (40/4 = 10), то сложность может дальше линейно вырождатся в O(|K/4|). Но на практике, это длинные ключи, с которыми хештаблицы и бинарные деревья работают вообще не оптимально, особенно по памяти. В целом на Worst Case случай достаточно сложно попасть, также как тяжело попасть в хештаблице на полное вырождение в связной список. И даже в этом самом плохом случае, она будет работать всеравно эффективней чем другие структуры (смотреть результаты бенчмарков с пометкой SEQUENCE)

Чувак, хочешь фокус покажу?

pastebin.com/3smXd8iA

Ололо, моя супер-пупер коллекция обгоняет всё известное науке! Волосы от неё становятся мягкими и шелковистыми! А ещё она решает ближневосточный конфликт и похмеляет с утра!

 assert(collection[i] = 42);

Всё, ты проиграл. Можешь не возвращаться.

Доёб к опечатке низащитан. С == тоже замечательно работает.

Доёб к опечатке низащитан.

«Проверка» от горе-кодера, который не в состоянии добавить -Wall или аналог и посмотреть, что же он написал, имеет отрицательную значимость. Как и его комментарии про «низащитан».

С == тоже замечательно работает.

Если бы ты хоть немного думал головой, а не другим местом, ты бы не палился на идентичных ключах. Повтори хотя бы для rand()... хотя нет, у тебя не получится, можешь не стараться.

Чувак! Не тупи ты так яросно, ну!


    1. -Wall такую ошибку в макросе не ловит, только в условном операторе. Иди читай ман по опциям gcc дальше
      Шутка, явно обозначенная таковой, не применимая на практике и работающая только за счёт оверкоммита, — это, разумеется, то самое место, где нужно искать опцию, которая позволит-таки поймать это предупреждение
      В коде нет ни одного ключа, идентичного другому. Идентичны только значения, которые могут быть вообще любыми. Если ты не отличаешь ключ от значения, то я сомневаюсь, что ты вообще способен хоть на какую-то осмысленную деятельность
      «Палево» здесь заключается не в значении ключей, а в настройках оверкоммита, что совершенно очевидно как из истории команд, так и из контекста, в котором я написал свой комментарий
  • Итого, чувак, не отличающий ключ от значения и не знающий, что такое оверкоммит, приходит рассказывать мне про отличие сравнения от присваивания. У тебя что, линтер вместо мозга?

    «- Господа, я всё придумал! Мы купим виноград, очень много винограда! 8 тонн! И положим его в огромный целлофановый пакет! Потом закопаем всё это в землю и через месяц выкопаем! Забродивший сок увезем в Москву, где откроем свой завод. И будем этим соком заправлять выдохшиеся фломастеры! Мы станем богатыми! Очень богатыми!
    — Первый канал представляет — ИДИОТЫ! По романам Федоров Михайловичей Достоевских.» © КВН, «ЛУНА» (Челябинск)

    -Wall такую ошибку в макросе не ловит, только в условном операторе.

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

    и работающая только за счёт оверкоммита

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

    Если ты не отличаешь ключ от значения,

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

    а в настройках оверкоммита

    Докажи. Более традиционной структурой, которая даёт эффективный поиск и использует такой же «overcommit». Пока что с твоей стороны это сплошной bullshit.

    не отличающий ключ от значения

    И снова — учись читать.

    У тебя что, линтер вместо мозга?

    У тебя, судя по последним постингам, мозга вообще нет. Или ты его засунул в зад вместо головы.

    Так он же написал про контекст. Что это была шутка с его стороны и ожидает что отнесутся как к шутке. Немного не качественная конечно, всетаки для более тонкого троллинга нужно было бы пофиксить «детские» ошибки, но тем не менее :)

    Так он же написал про контекст. Что это была шутка с его стороны

    Тогда он должен был и к моему исходному замечанию отнестись как к шутке. Он же вместо этого полез в бутылку.

    А учитывая, что вообще-то overcommit это тяжёлая палка о двух концах, и в каждой ОС решается по-своему и всё равно через одно место — тут совсем не тема для шуток.

    Воистину, хуже идиота только агрессивный идиот, упорствующий в своём идиотизме.

    Ну тогда тем более сам должен был отловить, или вместо макроса использовать шаблонную функцию.
    Какие шаблонные функции в C, родное сердце? Ты ещё и C от C++ не отличаешь?
    Чтобы доказать, что оверкоммит тут хоть как-то при чём...
    Достаточно посмотреть на размер аллоцируемой памяти. Если бы не оверкоммит, то процесс был бы прибит OOM-killer’ом.
    показать действительно аналогичную реализацию со сравнимой скоростью, которая бы что-то полезное делала, а не просто заливала память константами. Ты даже не попытался это сделать.
    Божечки! Ну перечитай ты тред, в который я отвечал. Мне реально западло объяснять суть анекдотов тем, до кого они не доходят даже с третьего раза.
    Докажи. Более традиционной структурой, которая даёт эффективный поиск и использует такой же «overcommit».
    Наркоман что ли? Полагаться на overcommit при реализации general-purpose структур нельзя, потому что его настройки могут быть разными. Что, кстати, и произошло выше по треду на QNX, где существуют требования реалтайма и поэтому lazy mapping недопустим.
    Воистину, хуже идиота только агрессивный идиот, упорствующий в своём идиотизме.

    О, ты посмотрелся в зеркало? Отлично, это первый шаг к правильным выводам. Только не останавливайся.

    Какие шаблонные функции в C, родное сердце? Ты ещё и C от C++ не отличаешь?

    А тебя никто не заставляет ограничиваться чистым C. А с учётом того, что тестируемый код на C++, вообще непонятно, откуда и зачем ты взял C.

    Мне реально западло объяснять суть анекдотов тем, до кого они не доходят даже с третьего раза.

    Хороший метод: сдулся — объявить всё сказанное шуткой и анекдотом. Боюсь только, не пройдёт.

    Полагаться на overcommit при реализации general-purpose структур нельзя, потому что его настройки могут быть разными.

    Теперь тебе осталось показать, что кто-то полагается на «конкретные настройки overcommit» при определении скорости.
    Если ты назвал таким «полаганием» использование крупноблочного new[], то тут как раз в типичном случае нет разницы с постепенным аллоцированием в результате наращивания объёма мелкими кусочками.
    А ещё это бы серьёзно повлияло на результаты в случае изменения размера начальной аллокации (значение параметра init()). Но этого не происходит, числа достаточно близкие и всё равно в выигрыше по сравнению с std::map.

    Из этого я и делаю вывод, что проблема std::map или в мелких аллокациях и недружественности к кэшу, или собственно алгоритме с недостаточно «плоским» деревом, или в обоих. Но чтобы это показать, нужно таки заставить их хотя бы пользоваться крупноблочным аллокатором. Ты это сделал? Нет, твоё чтение последовательного куска памяти может быть понято только как издевательство.

    Что, кстати, и произошло выше по треду на QNX, где существуют требования реалтайма и поэтому lazy mapping недопустим.

    Что случилось с QNX, надо обсуждать с Горчаком. Он хоть как-то осмысленно это обсуждает, в отличие от тебя. Но в любом случае это не основная проблема и явно не причина скорости реализации.

    О, ты посмотрелся в зеркало? Отлично, это первый шаг к правильным выводам. Только не останавливайся.
    А когда я ходил в детский садик, то там ещё любили говорить «я в домике». Рекомендую освоить. Как раз соответствует твоему уровню развития.
    А тебя никто не заставляет ограничиваться чистым C.
    Мой код. На чём хочу, на том и пишу. А C++ просто не нужен.
    А с учётом того, что тестируемый код на C++, вообще непонятно, откуда и зачем ты взял C.
    Чувак, вот ты правда думаешь, что я сравниваю что-то с кодом топикстартера? Вот правда?
    Хороший метод: сдулся — объявить всё сказанное шуткой и анекдотом. Боюсь только, не пройдёт.
    Ага, разумеется. Я это всё писал всерьёз. И всерьёз предлагал аллоцировать массив в 16 Гб.
    Теперь тебе осталось показать, что кто-то полагается на «конкретные настройки overcommit» при определении скорости.
    Ты сам мне это предлагал предыдущим постом. У меня все ходы записаны:
    Докажи. Более традиционной структурой, которая даёт эффективный поиск и использует такой же «overcommit»
    структурой, которая использует такой же «overcommit»
    структурой, которая использует «overcommit»
    Из этого я и делаю вывод, что проблема std::map или в мелких аллокациях и недружественности к кэшу, или собственно алгоритме с недостаточно «плоским» деревом, или в обоих.
    В обоих. Крачно-чёрное дерево имеет асимптотическую оценку сложности как вставки, так и поиска O(log n), где n — количество элементов. Кроме того, нелокальные прыжки от ноды к ноде по указателям инвалидируют кэш. Trie имеет оценку O(n), но n в данном случае — это длина ключа. Таким образом, корректнее сравнивать структуру ТС с хэш-мапой.
    Что случилось с QNX
    Можно повторить и на Linux, установив vm.overcommit_memory=2 и, опционально, ограничив vm.overcommit_ratio. На моей машине падение воспроизводится с ограничением памяти до 4 Гб.
    Мой код. На чём хочу, на том и пишу. А C++ просто не нужен.

    Вот это и есть твоё «в домике». Спасибо за подтверждение.

    Чувак, вот ты правда думаешь, что я сравниваю что-то с кодом топикстартера? Вот правда?

    Ну, если ты вообще тут решил посторонней хернёй заняться вместо оценки того, что делает ТС, то можешь дальше развлекаться этим без меня. Если захочешь вернуться к делу вместо пасочек — сообщи тут, а пока — прощавай.

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

    Что случилось с QNX, надо обсуждать с Горчаком. Он хоть как-то осмысленно это обсуждает, в отличие от тебя.
    Eh? Я же ясно сказал — сумасшедший расход памяти и стека. Скорость такой ценой не нужна.
    Я же ясно сказал — сумасшедший расход памяти и стека. Скорость такой ценой не нужна.

    Там начальные параметры показывают тот расход, можно подтюнить. Для многих задач (верю, что не для твоих) это лучше. Например, у нас сейчас типичный подход «аллоцируем всё по максимуму, а дальше не меняем», пусть оно даже 40GB займёт, но это оправдывается последующей скоростью (причём как суммарной, так и снижением peak latency), особенно с учётом Java GC (реально работает бутерброд из Java+C). И у нас тоже местами применяли trie для скорости лукапа. Поэтому и интересно, что же делает ТС.

    Для твоих же условий надо смотреть немного на другое — насколько оно будет адекватно работать с относительно небольшой начальной аллокацией, и расширением крупными блоками. Например, говоришь, что можно выделять по необходимости блоками по 256KB, при этом ограничить заполняемость снизу уровнем 70% — и пусть выкарабкивается из таких условий :)

    Если у тебя память ставится в главу угла, глянь реализации Trie на основе ацикличных графов — DAWG например. Эти реализации как раз сильно перекошены на экономию памяти с моделью доступа read-only.

    Неплохо.
    Тебя ожидает успех Малевича в программировании.

    Что такое memory overcommit ты знаешь

    Знаю. Что это самое дерьмовое грязное место во всей концепции современных ОС, и любая работа получается только тогда, когда хаки user land когерентны хакам kernel land. То, что у тебя получилось — это всего лишь их несочетаемость друг с другом. К качеству основного алгоритма это никак не относится.

    За 5 тестовых итераций статистика по page fault:

    Если всё равно быстрее конкурента, значит, этих фолтов не так уж и много?

    P.S. Всё, я пошёл мыть руки после такого кода...

    В описанных условиях — код более чем нормальный. На причесать найдётся народ. :)

    Что такое memory overcommit ты знаешь

    Знаю.

    Равно как знаю и то, что в десктопно-серверных ОС без него вообще сейчас не бывает. Запускаеш простенькую программку под обычной glibc, а она аллоцирует по 64MB на тред. Вот удобнее ей так, с целыми аренами общаться. 8 тредов — 512MB. А Бубен всего-то 256 одним рывком просил. Так кто прожорливее?

    просто на него надо обращать внимание, ибо иногда за это отрывают руки.

    Да, отрывают. Только не там и не так, как надо.

    Что такое Mach-styled VM, ты, наверно, знаешь. Вместе с его эффектами такого рода:

    1. Новый процесс. В память отображён бинарник и библиотеки. VSZ может быть
    дофига. RSS — только то, что изменилось (таблицы импорта в библиотеках
    плюс минимум данных и стека).
    2. Замапили гигабайтный файл. VSZ вырос на 1G. RSS не поменялся.
    3. Прочитали этот файл в памяти. VSZ не изменился. RSS — вырос на
    гигабайт или чуть меньше (если не сильно тесно).
    4. Подумали о жизни. В это время другим процессам потребовалась
    память. Половину кэша файла в памяти продискарили, VSZ не изменился,
    RSS упал на 500M.
    5. Форкнули из себя 10 копий. Все копии получили одну и ту же память
    кроме параметров в стеке (возвращаемый pid). Суммарный VSZ равен 10G,
    цена этой информации — 0 целых хрен десятых Суммарный RSS стал 5G,
    при этом реально в памяти занято только 500M.
    6. Одна из копий аллоцировала 100M и записала их данными обработки.
    Её VSZ и RSS выросли на 100M. Такой же рост у суммы.
    7. Эта копия форкнулась. Суммарные VSZ и RSS выросли на 1.1G, реальных
    затрат не поменялось.
    8. Новый форк переделал все данные в 100M области. Суммарные VSZ и RSS
    не поменялись, фактические затраты обоих выросли на 100M за счёт
    хранения копии.
    9. Ещё кому-то потребовалась память и система продискардила остаток
    большого файла в памяти. Суммарный VSZ не изменился. Суммарный RSS
    упал на 5.5G. Фактические затраты в памяти сократились на 500M.

    ну и так далее (это я взял пример из своего старого постинга).

    То есть тебе COW может стукнуть по голове в любой момент, асинхронно для исполняемого кода, без предупреждения.

    Возможно, в QNX этого нет. Тогда там любое добавление R/W памяти процесса, включая изменение режима на R/W, и любой fork должен проверяться на переполнение суммарной VM. Но если так, то шареная R/W память в нём — дикий расход суммарной VM. Даже когда ты просто подключил libc, таблицы её перемещений будут учитываться каждому процессу отдельно. Там же нет ключика mprotectʼу «вот это изменённое состояние теперь зафиксировать, для попытки следующих изменений создавать новый shadow object»? Гугл мне говорит, что нету. А значит — точно так же каменный век — или неэффективный перерасход VM, или возможность получить в лоб без предупреждения.

    Единственный качественный подход тут — увы, слишком сложный, чтобы его массово поддерживали — будет так:
    1. Заводить на процесс некоторый объём VM резерва в виде закоммиченных, но никуда не отображённых, страниц (без содержимого). Функции set/get управления объёмом этого резерва. По умолчанию размер 0 (аналогично текущему состоянию), сбрасывается в 0 у потомка после fork().
    2. При исчерпании VM (включая и общее количество, и различные групповые лимиты вплоть до rlimitʼа одного процесса) начинает расходоваться этот резерв.
    3. Оповещение о расходе этого резерва ниже сконфирурённого порога (оповещение сигналом, готовностью чтения спец. дескриптора — по обстановке).

    Вот если я где-то такое увижу — поверю, что там действительно думают о последствиях overcommitʼа. А без этого — как-то неинтересно, хоть там трижды «realtime» QNX.

    Равно как знаю и то, что в десктопно-серверных ОС без него вообще сейчас не бывает.
    Многие ли знают, что например в Windows 7 нет оверкоммита? Как нет? А вот так просто, его нет. Зато есть поддержка 2Мб страниц памяти, что сделало семёрку одной из самых любимых ОС за быстроту и отзывчивость. А также нормальную работу без свопа, кто его выключает в винде, тот поймёт.
    А Бубен всего-то 256 одним рывком просил. Так кто прожорливее?
    СУдя по отчёту волгринда он попросил пол гига и ему их дали, после он выжрал около полумегабайта стека, причём каким-то образом зацепив при это стек-гарда, тот ему этого не простил, ибо стек в QNX может увеличиваться динамически для основного потока, только если это происходит естественным путём, а не переполнением. Корка после гарда бесполезная и содержит обычный код, который повалился при обычном push rbp. В линуксе понятие переполнение стека очень мутное — оно подчиняется тем же законам, что и обычная память, а значит можно выжирать гигабайтами, пока не столкнёшься с данными или кодом.
    Гугл мне говорит, что нету. А значит — точно так же каменный век
    Этого нет в POSIX, а значит без него можно прожить, либо, как в данном случае — оно вторично — это попытка латать лодку во время шторма.
    А без этого — как-то неинтересно, хоть там трижды «realtime» QNX.
    Интересно или нет, но это не отменяет неработоспособности под QNX.

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

    Многие ли знают, что например в Windows 7 нет оверкоммита?

    Если вопрос из серии «удалось ли поразить собеседника?», то отвечу, что я это знал, но пару раз уже забывал, за ненужностью. Вообще вся система памяти в Windows в этом плане, да, прожорливее, но честнее. Одно из немногих её преимуществ :) которое, однако, компенсируется тяжёлыми заклинами при полном исчерпании (Linux пришлёпнул что-то и пошёл дальше, а тут может висеть часами).

    Зато есть поддержка 2Мб страниц памяти, что сделало семёрку одной из самых любимых ОС за быстроту и отзывчивость.

    В линуксе тоже есть поддержка. И не только large pages, но и huge pages :) Вот обсуждают улучшение от их работы.
    С другой стороны, large и huge pages это тупой хак (как почти всё у Intel). Чтобы получить large page, нужно аллоцировать 2MB одним куском, что может потребовать больших реаллокаций, а иногда и крайне сложно из-за того, что часть страниц неперемещаемы (wired). Лучше бы начали наконец увеличивать базовый размер страницы (сейчас оптимум около 64KB), или вообще сделали его настраиваемым в широких пределах, как в IA64.

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

    Если «сделали» это по сравнению с Windows XP, верю. Если по сравнению с лучшими конкурентами, там нет ничего особенного.

    после он выжрал около полумегабайта стека

    Вот это уже конкретно. Если это alloca(), то это проблема QNX, что она не подружила аллокатор со своим stack guard. Если что-то другое, надо поискать, что это было.

    Этого нет в POSIX, а значит без него можно прожить

    Уже смешно :) ты сам знаешь, что любой межвендорский стандарт это неизбежно только среднее по больнице.

    это попытка латать лодку во время шторма.

    Именно то, что я предложил, как раз лечение в самом корне. Вторично это выключать overcommit, делать ему хитрую логику и т.п. — это улучшает статистические показатели, но не лечит в основе (и в самых жёстких проявлениях).

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

    Это с чего такой вывод? Потомок после форка наследует один тред, да. И для устранения проблем от потери остальных (если это важно, а не просто exec() дёрнули) есть целевые методы. Родителю же без разницы, он продолжает работать как обычно.
    Давай точную цитату, или я считаю, что ты что-то недокурил.

    Если вопрос из серии «удалось ли поразить собеседника?», то отвечу, что я это знал, но пару раз уже забывал, за ненужностью.
    Я по поводу вот этой категоричности, цитирую:
    Равно как знаю и то, что в десктопно-серверных ОС без него вообще сейчас не бывает.
    В остальных сериях, 8 и 10 ввели dynamic memory из-за виртуализации, ибо она кушает много, но всё равно это далеко не тот убогий линуксовый оверкоммит.
    Вот это уже конкретно. Если это alloca(), то это проблема QNX, что она не подружила аллокатор со своим stack guard. Если что-то другое, надо поискать, что это было.
    Надо. Но как я уже сказал автору, ковыряться в данном гуано — это далеко за областями моего интереса, лучше это время уделить другим опенсоурс продуктам, где их гуано лежит ближе к душе. Кроме того QNX имеет замечательные инструментальные средства, которые доступны в трайал 30 дней, одно из них Memory Analysis и System Profiler (на базе иклипса). Любой, кому интересно может попробовать.
    Это с чего такой вывод? Потомок после форка наследует один тред, да.
    О боже, они таки реанимировали труп в новом посикс стандарте и вдохнули в него подобие жизни. Я этот момент пропустил. Раньше было проще — либо то, либо то. 20 с лишним caveats и ещё столько же implementation-specific и undefined операций. Труп стьюардессы нужно однозначно закопать.
    Я по поводу вот этой категоричности, цитирую

    Согласен. Windows у меня систематически не в контексте, поэтому забываю.

    В остальных сериях, 8 и 10 ввели dynamic memory из-за виртуализации, ибо она кушает много, но всё равно это далеко не тот убогий линуксовый оверкоммит.

    Почитаю, спасибо.

    где их гуано лежит ближе к душе

    :)

    О боже, они таки реанимировали труп в новом посикс стандарте и вдохнули в него подобие жизни.

    Ну логично же. Приходится совмещать, и пришлось таки совместить так, чтобы они работали.

    Меня в этом плане больше «радует» введение огромного количества новых атомарных (и нестандартных пока, увы) вызовов с опциями очереднаязюка_CLOEXEC. К сожалению, поезд слишком далеко ушёл, чтобы CLOEXEC автоматически ставить вообще на любые дескрипторы, даже настройкой уровня процесса с умолчанием на старом режиме(?) Вместе с другими соображениями (типа, nonblock должно быть опцией операции, в крайнем случае — дескриптора, а не ofile-сущности, *at() надо было планировать как можно раньше) — хочется таки машину времени.

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

    Замечательно, значит прикрываем тогда ветку Линукс, пускай Линуксоиды продолжают сидеть на старой и невероятно тормозной реализации std::map и ее аналогов.
    Ветка закрыта до тех пор, пока не найдется толковый человек который сможет рассказать в чем проблема.
    QNX я уж точно ставить не буду.

    Во второй половине поста указана проблема, ты просто её поскипал, так как не понимаешь о чём речь:

    dou.ua/...rums/topic/18849/#1006269

    Ну так я уже писал, что в Линуксах я почти полный ноль. Я вин дотнетчик. Дай ряд рекомендаций, как переделать реализацию, чтобы избавится от этой проблемы именно под QNX.

    Дай ряд рекомендаций, как переделать реализацию, чтобы избавится от этой проблемы именно под QNX.
    Старайся не есть стек, лучше выдели какую-то память заранее.
    Ну так я уже писал, что в Линуксах я почти полный ноль.
    Касательно скорость std::map под линуксом — я привёл статистику исключительных ситуаций — за первый тест около 60000 page fault операций, что как раз соответствует 60000*4096=~256Mb RAM. Т.е. каждый 25-й инсерт генерировал исключительную ситуацию, когда операционная система выделяла страницу памяти и подставляла её твоему процессу для заполнения. Естественно, что 25 инсертов выполняются гораздо быстрее чем подстановка страницы памяти. Вот всё время и профукивается на уровне ОС,а не в std::map.

    В лохматые времена реализация SGI STL содержала пул аллокатор с возможностью пре-аллоцирования памяти, т.е. ты знаешь, что у тебя будет 10млн элементов и ты мог заранее зарезервировать память у аллокатора ну или дать ему хинт, что вставлять будешь дофига, поэтому доаллоцировать он будет большими кусками, а не страницами по 4096 байт. С другой стороны я видел мало реальных использований std::map на больших данных, обычно пару тысяч элементов, тут скорость не принципиальна.

    В своём тесте для std::map ты можешь цикл с insert запустить два раза, во второму случае он просто сделает обновление записи, без каких-либо выделений памяти и можешь посмотреть на разницу в скорости. И вычислить сколько профукивается времени на реальное выделение памяти.

    Очень удобно все спихнуть на оператор new и издержки ОС внутри std::map, но тогда почему lookup операция в std::map еще медленее работает чем в HArrayInt/HArrayVarRAM. Разница в 10 и более раз на многих кейсах. Ведь там же уже ничего не выделяется по памяти. Мы просто находим и читаем нужный элемент.

    Очень удобно все спихнуть на оператор new и издержки ОС внутри std::map
    Извини, что пытался помочь. Удачи.

    Ты нормально поможешь если опишешь нормально условия при каких возникают проблемы.
    1. HArrayInt проблема только или с HArrayVarRAM тоже.
    2. Код теста желательно с локализацией примерно где есть фолт с страницей хотя бы приблизительно. Напр. на 1 млн все ок. А вот на 1.1 млн проблема
    3. Напишешь компилятор чем скомпилировал. Ось мы уже знаем.
    Хотя бы так.

    По STD::map я вроде как матан писал с сложностями алгоритма. Там ловить нечего.

    если на линуксе efence запользовать, то тоже падать будет
    mb@mb-work:~/tmp/HArray.UNX$ ./test
    === HArrayInt VS std::map<int,int> testing ===

    Electric Fence 2.2 Copyright © 1987-1999 Bruce Perens <[email protected]>
    Insert/Search 1000000 SEQUENCE keys (4 bytes each) ...
    HArrayInt => Insert: 11 msec, Search: 2 msec.

    ElectricFence Exiting: mprotect() failed: Segmentation fault (core dumped)

    Можно more details ?
    В идеале подебажить на какой строчке падает.

    В идеале подебажить на какой строчке падает.

    Самое полезное в треде, я только сейчас узнал об Electric Fence. Спасибо. Я пытался пригвоздить память в линуксе через mlockall(MCL_CURRENT | MCL_FUTURE); но оно ещё не работает — не сделали поддержку. Иногда, когда с QNX пересаживаешься на Linux, то бывает ощущение что попал в какой-то ретро мир :) где ещё не изобрели электричество :)

    Самое полезное в треде, я только сейчас узнал об Electric Fence.
    Вот, кстати, да. Одно это стоило прочтения треда.

    Конечно я не знаток

    Electric Fence
    , но вы не подумали что хрень это все.
    У вас есть кусок памяти. Вы его привели к одному обьекту класса, потом по условиям задачи к другому обьекту класса. Все легально с точки зрения работы с памятью, вы работаете только с тем что вам выделили, так задумано алгоритмом. И что же скажет на это Electric Fence? Расскажет что вы залезли за пределы какогото типа ?
    Самое полезное в треде, я только сейчас узнал об Electric Fence.

    Мой FAQ уже лет, наверно, 15 существенно не меняется, а ты его так и не прочитал :(

    Мой жизненный TODO содержит материалов лет на 200 изучения, так что я много чего ещё не прочитал :)

    А я приду и поломаю всем праздник.

    : g++ -o test HArrayVarRAM_delValueByKey.o HArrayVarRAM_getKeysAndValuesByRange.o HArrayVarRAM_getValueByKey.o HArrayVarRAM_getValuesByRange.o HArrayVarRAM_hasPartKey.o HArrayVarRAM_insert.o HArrayVarRAM_scanKeysAndValues.o Main.o stdafx.o -lefence
    : gdb ./test
    : run
    ...
    
    Program received signal SIGSEGV, Segmentation fault.
    0x00007ffff7014446 in strlen () from /usr/lib/libc.so.6
    : bt
    ...
    #5  0x00007ffff7bd6e0e in memalign () from /usr/lib/libefence.so.0
    #6  0x00007ffff78dca48 in operator new (sz=sz@entry=40) at /build/gcc/src/gcc/libstdc++-v3/libsupc++/new_op.cc:50
    ...
    #13 testStdMapInt (keys=keys@entry=0x7ffff4750600, countKeys=countKeys@entry=1000000) at Main.cpp:94
    #14 0x000000000040735c in HArrayInt_VS_StdMap_IntKey (startOnAmount=startOnAmount@entry=1000000, stepOfAmount=stepOfAmount@entry=2000000, 
        stopOnAmount=stopOnAmount@entry=10000000) at Main.cpp:156
    #15 0x0000000000407ae8 in main () at Main.cpp:548
    :frame 13
    #13 testStdMapInt (keys=keys@entry=0x7ffff4750600, countKeys=countKeys@entry=1000000) at Main.cpp:94
    94			mymap[keys[i]] = keys[i];

    Пытаемся понять, что именно там происходит.

    : strace ./test > test.out
    : grep mprotect test.out | tail
    ...
    mprotect(0x7f9c8291b000, 1323008, PROT_NONE) = 0
    mprotect(0x7f9c8291b000, 1323008, PROT_READ|PROT_WRITE) = 0
    mprotect(0x7f9c826bc000, 4096, PROT_READ|PROT_WRITE) = -1 ENOMEM (Cannot allocate memory)
    : grep mprotect test.out | wc -l
    132421

    Смотрим mprotect(2), 3-я причина для ENOMEM. Пересобираем без -lefence, strace, grep, 20 mprotect’ов. Причина в efence, а не в тестируемом коде.

    Убираем лишние нули в main, пересобираем с -lefence, всё проходит.

    Дико извиняюсь. Мне намекнули, что это мог быть стеб.
    Так есть баг в HArrayInt ? Стоит Linux + Efence ставить и юзать или нет ?

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

    Efence стоит. Но не понимаю смысла для вас. Я не знаю, какой вы алгоритмист, но программист из вас не очень. Соответственно, как по мне, вам лучше сделать статью и залучить сообщество.

    сообщество поможет отрефакторить код

    [sarcasm on]
    "Гении господствуют над хаосом, бездарности стремятся к порядку"©
    [sarcasm off]

    В Вас действительно теплется надежда чтото там серьезно отрефакторить без ущерба производительности ?

    Я думаю, що все реально. Очікуємо на статтю.

    Багов, которые может показать efence, в HArrayInt нет. У efence есть ограничение на количество malloc’ов. Тест вида «давайте сделаем 100K+ insert’ов» споткнулся об это ограничение.

    Поставить что-то для контроля проблем с памятью стоит. Именно efence — наверное нет, valgrind покажет всё, что может показать efence, плюс кое-чего сверху, и он удобней.

    Сделай, я не шарю и иду кататься на велосипеде с зеркалкой.

    Если есть баги пишите Step To Reproduce и заводите Issue на GitHub.
    По моему мнению там багов нет вообще. Протестировано в двух движках баз данных под высокими нагрузками и на больших обьемах в течении нескольких лет.

    Трое офицеров играют у одного из них дома в карты. Только раздали, вдруг из детской выскакивает сын хозяина и кричит:
    — А у папы два туза!
    Делать нечего, раздали по новой. Снова выскакивает сын и кричит:
    — А у капитана червовый марьяж!
    Снова пересдали. Снова выскакивает сын и кричит:
    — А у поручика...
    — Погоди! — перебивает поручик Ржевский , и уводит мальчика в детскую.
    Через пару минут возвращается, садится играть дальше. Проходит 5, 10,
    15 минут — мальчика нет. Хозяин квартиры спрашивает:
    — Поручик, Вы что, его побили?
    — Как можно, я же офицер!
    — А что же Вы сделали?
    — Я его дрочить научил

    ТС, исправь код чтоб под линуксом из коробки собирался без бубна

    Если нет линукса дома, на Azure за несколько долларов можно взять виртуалку.

    мейкфайл постараюсь добавить.
    Доступ к Линуксу есть.

    я бегло посмотрел, но думаю знаю, что вы делаете, ибо сам подобное делал и не раз (любую конкретную задачу можно решить лучше, чем обобщенную). вы решаете разные задачи:
    — вы сразу выделяете память
    — у вас фиксированный тип данных
    — std заморачивается на exception
    — проводить тесты надо через 2 отдельных приложения, плюс выполнять их много раз
    и так далее

    в целом похвально, только вы явно выбрали не того «противника».

    я бегло посмотрел, но думаю знаю, что вы делаете, ибо сам подобное делал и не раз (любую конкретную задачу можно решить лучше, чем обобщенную). вы решаете разные задачи:

    Нет, ценность этой реализации именно в сбалансированости.
    По ходу пьесы у меня было множество «перекошеных» реализаций, которые лучше делали вставки, или поиск, или лучше были по памяти, или лучше работали с определенными датасетами ключей.
    Но я добился именно оптимальной сбалансировасти реализации. Сейчас он как поршневой затвор калашникова, работает просто быстро и надежно практически в любых условиях.

    — вы сразу выделяете память
    Потому что там по сути встроен свой эффективный менеджер памяти, который запрашивает память по-странично.
    у вас фиксированный тип данных
    Не фиксированый. В одну и туже структуру можно добавлять ключи разной длины, но они выровнены на 4 байта.
    — std заморачивается на exception
    std::bad_allock и я пробрасываю. А других эксепшинов там по идее не должно быть.
    проводить тесты надо через 2 отдельных приложения, плюс выполнять их много раз
    и так далее

    Проводите. Тесты третьей стороны всегда хорошо.

    в целом похвально, только вы явно выбрали не того «противника».
    Предложите другую структуру, только чтобы она была универсальна.

    Открою страшную тайну....у компилера есть ключи для выравнивания памяти автоматически всего и везде, а также прагмы.
    Эффективный манагер памяти ... собственно это тоже решается путем линковки с другими либами (например: goog-perftools.sourceforge.net/doc/tcmalloc.html или передачей в качестве параметра своих аллокаторов для std контейнеров.

    Так же std контейнеры умеют пре-выделять память, например vector::reserv()

    Ну круто же. Но судя по бенчмаркам у меня сделано еще круче.

    В ваших бенчмарках вы влоб все делаете. Попробуйте стд-мап дать другой аллокатор, например вот из пулла буста: www.boost.org/...nterfaces/pool_alloc.html
    И сравните, как изменится ее скорость, с другими аллокаторами. По умолчанию, кстати, new/delete потоко безопасны (мутексы там), а malloc/free нет. Не смотрел, что у вас там, но замена new на malloc по идее тоже ускорит на 1 потоке. Но тогда не сработает способ с аллокаторами, тогда нужно менять либы (см выше).

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

    Существенно не поможет. Можете попробовать.

    Да не буду я пробовать — снова скажите сломал. Сделайте нормальную сборку, чтоб ниче там не трогать, кроме тестов.

    Я надеюсь STD::map не сломаете и оживите его хотя бы на процентов 15.
    А мне там делать нечего, Бо я понимаю что в терминах О большого там ловить особо нечего. STD::map тут выступает как еще одна унылая реализация красно-черных деревьев с мат базой времен студенческой молодости Стоунбрейкера

    давайте так, конкретная реализация деревьев в стандарте не прописана, если микрософт проебал — все к ним, гцц реализация в 50 раз быстрее вашей.

    А мужики то не знали.
    Вот только что уже часть портанул и действительно разница в 50 раз в некоторых кейсах на Убунте. Только в обратную сторону. Внезапно, но GCC мои гоутушечки тоже по душе пришлись. Хе Хе

    Я кстати могу протестить твой код на живом примере, но мне нужна реализация на С, а не просто интерфейс. Сейчас работают BSD RBTREE макросы быстрее в 2.5 раза чем GNU C++ реализация std::map и быстрее раз в 5 чем LLVM С++ реализация. Задача — хранить связки физического адреса памяти и виртуальной памяти для одного и того же участка для хоста и для гостевого клиента (разные виртуальные адреса), если элемента в списке нет, то мапится память и адреса вносятся в список и так далее. Цель — не мапить повторно то, что уже замапленно. Это обычная задача виртуализации GPU. За 1 фрейм можно намапить до 3.5Gb памяти и в конце фрейма освободить её, чтобы вначале нового фрейма замапить заново, но уже, возможно, по другим адресам. Операция очистки списка должна быть не хуже, чем максимальное время поиска.

    Для постоянного римапа 1Gb памяти специальный тест выдаёт около 115FPS в гостевой ОС. Можем потестировать твою поделку, но только С реализация.

    А на Lua тебе не переписать случайно ? Ну или Руби Паскаль Делфи чтобы удобней было тестировать мою «поделку».

    А если серьезно — вам надо вы портируйте на Си. Тем более из С++ там гулькин нос, я не люблю оверинжиниринг.

    Если бы были мозги, то ты бы это сделал в первую очередь. Не мне ведь это нужно, меня скорость BSD RBTree устраивает вполне. Но если бы мне предложили для моего кода такое испытание, я бы сделал всё возможное. Тем более я мог бы дать лишний шанс засветиться в QNX и Intel как раз среди людей, которые хайрят таланты, если бы результаты были стоящие. Так как это самое узкое место.

    если бы результаты были стоящие
    Про результаты можно почитать тут:
    forum.ixbt.com/...aram=1&posts=&id=26&id=40
    Топики один другого забавней.

    Еслибы да кабы. О чем спич ?
    Почему написано на Си++, а не на Си ? Серьезно ? А что есть принципиальная разница ?

    Коментар порушує правила спільноти і видалений модераторами.

    Еслибы да кабы. О чем спич ?
    Ни о чём, спи дальше.
    Почему написано на Си++, а не на Си ? Серьезно ? А что есть принципиальная разница ?
    Разница в моём времени, я и так его собирался потратить на поиск более быстрого алгоритма, но тратить на переписывание того, что может не взлететь я не хочу, для этого есть автор.
    Разница в моём времени, я и так его собирался потратить на поиск более быстрого алгоритма, но тратить на переписывание того, что может не взлететь я не хочу, для этого есть автор.

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

    Моё дело предложить...

    Коментар порушує правила спільноти і видалений модераторами.

    а malloc/free нет
    Согласно POSIX.1-2008, всё объявленное там thread-safe.

    в конкретно взятом сегодня снапшуте мес 1 назад обновы archlinux malloc работает норм, при компиляции того же кода на маке — фейлит ...потоками.

    Посикс тут очень жёсткий, если завёл потоки — делай всё thread-safe. Не можешь — убирай потоки. Ну а OSX посикс сертификация только базовая была, поэтому они и так не-посикс ОС.

    Но откуда там Error для рандом ?
    Вносили снова правки ?

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

    Тут тоже весело: forum.ixbt.com/topic.cgi?id=26:43056
    пара особо забавных перлов


    C 30 млн какойто баг, нужно смотреть.
    А вот с 25 млн ключей.
    .....
    Стабильно мой код более эффективныйболее чем в 10 раз.

    Bazist
    Скорей всего у тебя нет файлика c:\\rand2.avi с которого генерятся рандом ключи.

    Естественно у меня нет файла це:\\ранд2.ави

    upd: forum.ixbt.com/...aram=1&posts=&id=26&id=40
    аффтар походу таки знаменит на ixbt :)
    Бубен много где знаменит своей упоротостью.
    Я там рандомно ткнул в разные топики — это не упоротость, этот какой то мазохизм. Его макают там постоянно, а он с пафосом вещает что все вокруг дураки и не шарят — один он светоч.
    Ни разу не выдерживает сравнения с тем же Крисом. На том же rsdn с ним прикольно и весело общаться. Ни тебе снобизма с его стороны, ни массовой одноголосой критики со стороны других чуваков.

    ну вот собственно говоря ты и неплохо срезюмировал причины по которым крис известен, и успешен, а бубен — клоун.

    Сам ты клоун. У меня есть грядка которую я неспеша окучиваю.
    Это поисковые алгоритмы и базы данных. На других грядках я не пасусь,
    ну и на своих грядках дилетантов жаловать не буду.

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

    Острая, жгущая боль в нижней части спины лишила тебя способности писать связный текст?

    Тебя послали на три буквы только что

    во припекает. Я даж не ожидал. Совсем поплыл Слава :)

    Да, мыщъх уникальный экземпляр.

    когда я читаю топики бубна- мне все время кажется что он подрожает мыщъх, но только как- то слабо получается пока что.

    Блин, совсем забыл, кто там страдал под Linux. Я же несколько месяцев назад сделал полный порт DniproDB под Linux. В проекте под code::blocks все компилится и запускается.
    А если в консоле набрать db.SelfTest() то запускает 9 млн запросов в 11 бенчмарках.
    Кому этого мало, можете отдельно чудо-мапу отсюда попытаться выковырять, это не сложно

    github.com/Bazist/DniproServer.UNX

    Есть люди, которые собирают лайки на фоточки в инстаграм, а вы, походу — число форков на гитхабе. Случайно не начальнику крутость показать? :)
    ...уже все потестили. Дите ваше — пеленки сами меняйте. Зачем нам еще раз тестить ваш мусор, если вы игнорируете результаты. Т.о. образом налицо манипуляции, с неизвестной целью. Вероятно — набрать форков и тыкнуть их в нос кому-то, например начальнику, чтоб не уволил, или, у вас 5 шагов отрицания — «злость, отрицание, смирение..». Качество кода вас совершенно не волнует, исходя из ответов.
    А нас не волнуют ваши проблемы личности — или слушайте и учитесь, или хватит. Работать пора.

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

    не дай Бог, как ты писать....вот давай конкретный пример, по твоему тексту.
    Объясни мне убогому, сколько бит в типе uint?

    #define uint unsigned int

    Хорошо, это ты выучил, почти :)
    Правильный ответ: не меньше чем 16
    А кто сказал, что процессор 32 бита? В телефонах (и не только) стоят Arm, которые имеют режим thumb на 16 бит, и он предпочтительнее, т.к. работает обычно быстрее.
    Т.е. 1 телефон, 1 программа, в ней сразу могут быть смешаны 16-32-64 бита режимы процессора.
    Почему там у тя в коде везде ожидается ровно 32 ? С каких таких?
    Вот собственно «мои убогие правки» и были — я задал ровно 32 бита. Кстати правки ты так и не посмотрел. Вот тебе еще раз — мои испрвления выделены:
    github.com/...374799f13d4ed7fda99cbfc88

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

    А кто сказал, что процессор 32 бита? В телефонах (и не только) стоят Arm, которые имеют режим thumb на 16 бит, и он предпочтительнее, т.к. работает обычно быстрее. Т.е. 1 телефон, 1 программа, в ней сразу могут быть смешаны 16-32-64 бита режимы процессора.
    Почему там у тя в коде везде ожидается ровно 32 ? С каких таких?

    А потому что я не решаю сферическую задачу в вакууме.
    32 бита и работа под 32х битные регистры, для конкретно этой реализации краеугольный камень. Везде используются метрики экспансии блоков, выравнивания, офсетов, партицирование ключей, хеширования в четыре слота, специально под 32х битные процессы. Если у тебя Integer вдруг будет не 32 бита, а 16 бита или 64 бита, то это уже совершенно другой адаптированый алгоритм.
    Да и я думаю, что если у тебя int окажется 16 бит, то на гитхабе вылетит с ошибками примерно 90% программ. Даже обычный унылый классически for(int i=0; i < big_num; i++) просто зависнет намертво.

    Вот тебе еще раз — мои испрвления выделены:
    github.com/...374799f13d4ed7fda99cbfc88
    Вверху есть проект. Там есть все теже файлы HArrayVarRAM_чтототам, возьми сделай элементарный diff или скопируй их себе и будет тебе счастье.

    Зачем оно мне? Я уже сделал, скопировал, запустил. Если ты считаешь я накосячил — проверь мои исправления. Если я все сделал ОК, тогда std::map wins. Кстати не я 1 делал, там другой чел тоже отдельно от меня портировал, и даже сделал exe-32 бита, но компилятором GCC. Там твой алгоритм еще сильнее всосал, в 3 раза медленнее вышел, чем под линукс.

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

    блин) ты ваще даже не смотриш...гдеж там сотня, если исправил 5 строчек. Ну и сами тестовые функции, которые ты говриш не важны.
    Вот нахер с таким тобой крутым беседовать. Тебе показываешь результат, а ты уши зажал -"бла-бла-бла, я в домике".

    Тем более. 5 строчек исправить чтобы портировать правильно алгоритм под Линукс этого мало. Остальные 95 остались неисправленными. Копипаст тебя спасет.

    Ok, удалил fork тоже. Как сделаешь сборку командой cmake ./ свистнешь. Гляну.

    Про АРМ и Thumb бред. Полный.

    Жаль, тролей внизу развелось как куропаток на птицефабрике.
    Потому покидаю секции внизу, и представляю результаты последних тестов.
    Чтобы их проверить, достаточно скачать мой проект под Visual Studio и запустить тесты.
    Качаем, запускаем и проникаемся всей красотой игры действительно эффективных реализаций.

    github.com/...ter/HArray_Benchmarks.txt

    Скачав, відкрив, скомпіляти не можу — каже не та версія студії.

    У меня стоит Visual Studio Express 2015 for Desktop
    Качал отсюда, она бесплатная.
    www.visualstudio.com/downloads

    И живёт 30 дней. Я лучше Code::Blocks применю, оно кроссплатформенное и без извращений.

    Вот я не знаю насчет С++, но у нас в Джаве не принятно шарить проекты какой-то IDE — для этого у нас есть Maven/Gradle, которые прекрасно импортятся во все основые IDE и вообще можно даже без неё запустить. У вас такого нет?

    Для крестов общепринятый подход — нечто, что генерирует makefile. CMakeList.txt, напирмер. Совсем олдскул — автолулзы, но это принято только в GNU-мире.

    Ничего не понял. У кого там не получилось скомпилировать или смигрировать под Линукс ?
    В 2013 году собирал версию в code::blocks за несколько часов
    Солюшин
    github.com/.../tree/master/VymaDB_Linux

    Теже бенчмарки под Linux, за тотже год
    wiki.pikosec.com/...ayFix_VS_GLib::GHashTable

    По основному солюшину, сегодня зачекиню код.
    Там будет 45 разнообразных тестов.
    (Инты, Строки, Бинари ключи. Рандом, Последовательные, с Периодом и тд).
    Крутил вертел уже как хотел, быстрее работает во всех тестах от 3х до 20раз чем std::map

    PS: GCC кстате более качественно компилит мой код, чем Майкрософтовский

    Эксперимент можно заканчивать. На 20мил у меня 8Гб не хватило ...все зависло наглухо, пришлось REISUB юзать ...

    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 13756 msec, Search: 100 msec.
    std::map => Insert: 44 msec, Error
    ...еще много Error
    Search: 1110 msec.

    Небольшие фиксы, до компилабельного cmake состояния тут:
    github.com/alexzk1/HArray

    А так — код ужасен, как по мне, и куча фактических ошибок. Утечек памяти и прочей фигни, во всяком случае в Main. Но судя по варнингам, не все хорошо и в либе самой. Старался не трогать. Так же...подозреваю будут проблемы, например, на arm, где 16/32/64 бита могут быть в 1 программе. Еще бы я погонял кросс-проверку, а правильно ли оно вообще данные хранит, т.к. там полно срезания битовости, вроде в член класса 8 бит суется 32 бита... но лень.
    --------------------
    Прогнал еще для ляма:

    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 399899 msec, Search: 309485 msec.
    std::map => Insert: 35253 msec, Error
    ...еще много Error на пару килобайт
    Search: 1141631 msec.
    --------
    Без оптимизации
    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 45848 msec, Search: 239 msec.
    std::map => Insert: 207 msec, Error

    Для gcc отримую пачку помилок типу:
    error crosses initialization of ‘uint& keyValue’ .... ......
    Часу і бажання в тому розбиратися немає.

    P.S. Відповідь є тут: stackoverflow.com/...-switch-statement/8550253

    Автору варто почитати

    -fpermissive добавить
    там goto прыгает через объявления переменных, там ваще ХЗ че в итоге из этого выйдет. Скорее всего это UB. Но в нескольких экранах «так должно» лень копать.
    А вообще — склонируйте мой — там компильнулось.

    Та ж історія і куча error

    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 62078 msec, Search: 108 msec.
    std::map => Insert: 65 msec, Search: 110 msec.

    P.S. Помилки пофіксив, автор взагалі не знає як працює strcmp

    правильно так:

    bool operator<(const Str& a, const Str& b)
    {
      return strcmp(a.Val, b.Val) < 0; // а не  strcmp(a.Val, b.Val) == 1
    }
    

    так что так, да. std::map wins!
    Мне не понятно, как в массив размера 1 он совал 20лямов? В оригинале так. М.б. там лютый мусор был и казалось по логу все быстро и верно? ))) Ну еще вот те goto смущают...

    ...о..а я и прощелкал сравнение :) глянул, что оно мне не нравится, но плюнул ..

    Спеціально провів кроскомпіляцію під x32 (додав опцію -m32), результати cхожі:

    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 48796 msec, Search: 101 msec.
    std::map => Insert: 325 msec, Search: 198 msec.
    

    Для чистоты эксперимента, этот патченый код бы VC компильнуть и сравнить, что автор писал. Возможно, UB давали крутые оценки,а возможно std::map от MS фигня... но у меня винды нет :) А ВЦ-6 есть...образ))

    Угу, добавил фикс оператора, пропали ошибки (-О3)

    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 13034 msec, Search: 69 msec.
    std::map => Insert: 297 msec, Search: 168 msec.

    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 12187 msec, Search: 84 msec.
    std::map => Insert: 309 msec, Search: 163 msec.

    === HArrayVarRAM testing ===
    Start benchmarks ....
    Insert/Search 1000 random keys (256 bytes each) ...
    HArrayVarRAM => Insert: 15476 msec, Search: 206 msec.
    std::map => Insert: 281 msec, Search: 173 msec.

    И меня смущают такие резкие разбросы его алгоритма, до 30%, чета там не то.

    Не поможет. Аффтару надо начать с Макконнела, затем перейти к Меерсу и Саттеру. ПОтом возможно ему станет стыдно за этот пост. :)

    Не поверете...сам читал 2 книги всего по ++ - «Справочник по С и С++» 1997г и еще 1, забыл название, об общем проектировании программ, с примерами на С++ и критикой MFC. Вот недавно купил «Эффективное С14++», помоему оно мне повредило :) Напихал std::move, куда не стоило. В итоге откатывать пришлось. А так — практика и man.

    Имха тут все индивидуаллно. Ну а в случае топикстартера похоже что практики недостаточно.

    Говнокод и проблемы идут рядом чуть чаще чем всегда. Респект что не поленился заранать это говно, мне было откровенно лень.

    Да ну, весело же. О С++ уже года три пофлудить не с кем :)) - все яваскрипты какие-то...хых.

    Ну уж оцените, сильно ли сломали

    github.com/...374799f13d4ed7fda99cbfc88

    К сожалению в данный момент у меня нет Линукса под рукой.
    Попробуйте взять солюшин тот который я когдато мигрировал и отлаживал в code::blocks.

    а все потому что про CI ты ваще ни в дуб ногой: github.com/....WIN/tree/master/Releases

    Вам не нужен линукс — оцените мои правки в код, не ломают ли они мега-мысль, если не ломают, то мои тесты уже здесь опубликованы.

    Судя по результатам тестов — ломают очень серьезно.
    А чтобы найти что. Нужно потратить N часов с дебагером и под Линуксом, которого у меня нет.
    Мне это также тяжело сделать, как уам запустить мой проект под Visual Studio
    и убедится во всем всеголишь по нажатию клавиши F5.

    Давайте начнем вот с этого:
    Str keys[1];
    +std::shared_ptr<str> keys;

    смысл в том, что у вас резервируется память на 1 элемент, а загружаем туда 20 000 000, не уверен, почему это вообще работало. Но пока представляется ключевым моментом.
    Так же стоит оператор < исправить на

    bool operator<(const Str& a, const Str& b)
    {
    return strcmp(a.Val, b.Val) < 0; // а не strcmp(a.Val, b.Val) == 1
    }

    Сделайте на своей VC и перетестируйте.

    Внезапно, но это не принципиально. Иначе было бы на консоле Error. Коллекция будет в ASC или DESC виде.
    Результаты работы с вашей функцией


    === HArrayVarRAM VS std::map<strkey,int> testing ===
    Insert/Search 1000000 SIMILAR keys (64 bytes each) ...
    HArrayVarRAM => Insert: 463 msec, Search: 403 msec.
    std::map => Insert: 2425 msec, Search: 2328 msec.

    Insert/Search 2000000 SIMILAR keys (64 bytes each) ...
    HArrayVarRAM => Insert: 940 msec, Search: 1076 msec.
    std::map => Insert: 5232 msec, Search: 5012 msec.

    Insert/Search 3000000 SIMILAR keys (64 bytes each) ...
    HArrayVarRAM => Insert: 1508 msec, Search: 1718 msec.
    std::map => Insert: 8607 msec, Search: 7981 msec.

    Insert/Search 1000000 RANDOM keys (64 bytes each) ...
    HArrayVarRAM => Insert: 421 msec, Search: 382 msec.
    std::map => Insert: 1441 msec, Search: 1359 msec.

    Insert/Search 2000000 RANDOM keys (64 bytes each) ...
    HArrayVarRAM => Insert: 1190 msec, Search: 996 msec.
    std::map => Insert: 3490 msec, Search: 3082 msec.

    Insert/Search 3000000 RANDOM keys (64 bytes each) ...
    HArrayVarRAM => Insert: 1556 msec, Search: 1529 msec.
    std::map => Insert: 5225 msec, Search: 5033 msec.

    Press any key to continue . . .

    Остальные тесты я уже тоже залил, можете ориентироваться на эти 45 тестов
    github.com/...ob/master/HArray/Main.cpp

    А вы размер массива поправили под тесты-то? Выход за границы памяти это пустяк?

    И да, с вашим оператором сравнения не верным так и было — 10кб слова Error

    Кстати, вы знаете, что такое «Undefined Behaviour» ? Это ваше «сломали» и есть. Когда вы нарушаете принципы стандарта языка, то что-то в итоге будет, но никто не гарантирует, что оно повторится одинакова на других машинах и(или) компиляторах. Поэтому вы и ковыряете дебугером сутками, что мы сходу нашли, как минимум 3 UB. Один из них в методе insert — goto. Но его трогать не решились, чтобы не ломать эксперимент.

    Тот код который был в Main, это муссорный код который я копировал и разбирал как «контрпример» который мне «вбросили», о чем писал ниже, про то как разоблачил мошейничество.

    Послушайте. У вас в методе инсерт есть куча goto. Оно пропускает создание и инициализацию локальных переменных, что в итоге будет с вашим алгоритмом зависит от положения звезд на небе и фазы Юпитера. Кстати, это косвенно подверждают мои тесты — разброс вашей функции до 30% на моем компе, и до 3х раз на разных компах, причем std::map с этими же данными дает 3-5% разброс времени во всех случаях.

    Нет. Там все продумано. Еще раз.
    Возьмите те сорцы под Линукс которые я вам дал.
    Вы не знаете все ньюансы компиляции под Линукс и тем более самого алгоритма. Допустили ряд ошибок и пытаетесь чтото оспаривать после своих модификаций.

    github.com/.../tree/master/VymaDB_Linux

    т.е. вот это
    const uint MAX_SHORT = 65536;

    в хидере вы считаете нормальным?

    «В хидерах не может быть инициализации, только декларации». Обычно на это все ругается линкер — что у вас переменная инициализирована N раз (по числу include), можно это дело обхитрить и получить не определенное поведение.

    У меня уже нет сил с вами спорить.
    Просто возьмите то что я дал и проверьте.

    Какой код, о чем ты говоришь ?
    Он написан именно так, чтобы работать быстро.
    Поверь мне, у меня 14 лет активного опыта работы на самых разных языках, если нужно написать код красивый ,то я пишу так как надо. Но это не тот случай !
    Если вы не разобрались за 1 день так не расстраивайтесь.
    Я его писал 5 лет и часто думал что вообще это невозможно написать. Но написал. И есть результат.

    ....он ВОЗМОЖНО быстр и ВОЗМОЖНО работает только на каком-то ОГРАНИЧЕНОМ множестве машин, скорее всего только на твоей 1. Потому что он не верен с точки зрения языка и компилятора, а ты его подогнал под конкретные условия. Но вобщем и целом он мусор, а ты называешь это «сломали, бывает».

    А давай ты попробуешь, скачаешь и померяешь а потом напишешь свое мнение. Ты умный парень, мне не хотелось бы чтобы ты ишел на поводу какогото персонажа, которые пытался за 5 минут мигрировать под Линукс, наломал кучу дров и отказывается брать правильный исходник.
    Ок? Репо склонировали больше 10 раз и не только он мерял, кстате. И если была бы липа, мне бы уже написали сто раз, как с тем контрпримером. А так — да, смешно.

    ну...скачал
    github.com/Bazist/DniproExamples
    взял папочку линукс, ее файлами перезаписал мой тест (что уже сеня делал):

    y/HArrayFix_del.cpp: In member function ‘virtual bool HArrayFix::del(unsigned int*)’:
    /home/alex/Work/OpenSource/HArray/HArray/HArrayFix_del.cpp:24:6: error: ‘ContentCell’ was not declared in this scope
    ContentCell& contentCell = pContentPage->pContent[contentIndex];

    ^^^^^^^^^ то починил, кеш...
    сбросил кеш, терь новое

    /HArrayVarRAM.h:64:2: error: ‘VarPage’ does not name a type
    VarPage** pVarPages;
    ^~~~~~~

    Только эта ошибка ? Я уже не помню возможно ключи в компиляцию нужно еще прописать

    типы не объявлены, их просто нигде нет
    HArrayFixPair
    VarPair

    Кароче я хз шо ето ты за код для линукс дал, но тут не хватает типов. А я просто твои тесты хотел погонять с твоими правильными фиксами)
    Удалил нахрен это барахло все с компа...

    Ладно чувак не парься. У меня вечер забит сегодня, но если будет время я специально под линь скомпилирую и узреют Никс красноглазики истину.

    Лолшто. Там нет багов. В этот алгоритм вкачивали 160 млн ключей. Он индексировал терабайты в бубенкоме. Его прогинали в зверских тестах Днипры. Да там уже третий год чище чем в банке с спиртом.

    та да, код настолько идеален, что даже если макросом заменить тру на фолс, то все как работало так и будет, такое только за 14 лет на разных языках можно написать, если не разобрался за всю жизнь — не надо расстраиваться, этот код написан чтобы работать быстро как ядро, а не чтобы быть понятным хоть кому-нибудь

    Кстати, если мерять у кого длиннее...первую свою программу я написал 30 лет назад для микрокалькулятора МК-61 ))

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

    Ну у тебя то хоть в Ипаме винда должна быть, дотнетчик. Не ? Померять слабо ?

    у меня макось :)
    и опять таки — что мерять то? рандомные результаты?

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

    на его компе
    Тоесть у меня какойто особый компилятор на машине, и особый комп ? Спасибо. Улыбнуло.

    Вы не поверите....именно так. Запустите на другой машине, с другой видеокартой и размером оперативы и вы удивитесь.

    с другой видеокартой и размером оперативы и вы удивитесь.

    Ржач. Уже и видеокарта имеет значение !
    Алгоритм писали примерно в течении 6 лет и запускался он на нескольких разных модификациях. Ну не меньше 10 так точно. Только 4 ноутбука у меня домашних поменялось. Плюс рабочие машинки и тд.

    Значение имеет разметка памяти в системе — самый простой вариант ее изменить я уже описал. Можно покруче — скомпилируйте под распери-пи, там есть винда 10, но процессор арм, раз вам сестра ноут с линуксом не дает.

    Да да да.
    Существует конфигурация с особой видеокартой которая оживит std::map в десять раз. Шутка дня. До пятницы бы хотябы дотянул.

    особый комп у всех, если ты не знал, это же не минералка в бутылках, он настраивается по-разному и железом отличается

    Осталось тебе найти и показать конфигурацию того особого компа. Дабы мы смогли наконец узреть контрпример

    любой другой, в идеале чтобы архитектура процессора отличалась от твоей, чтобы оно даже не скомпилировалось

    абрвалг!
    Скомпилируй под анроид. Android-ndk.

    И еще, std::map у микрософт и в gcc это совершенно не зависимые и разные алгоритмы. Никто не говорит, что у микрософт самый быстрый. Более того, столь значительные расхождения времени вашего алгоритма у вас и у нас (в разы!) говорит о том, что он просто не верен с точки зрения языка. Вы его «заточили» под конкретный компилятор, игнорируя все ошибки. На 16 бит или 64 бит даже этим же компилятором, скорее всего, снова не заработает.

    std::map в любой своей модификации тут выступает скорей в роли статиста. На самом деле тут борьба мат моделей и оспаривается даже скорость работы не только RB и прочьих двоичных деревьев, но даже Хештаблиц (включая GLib).

    Все. Устал. Не прошибаемая глупость.
    Боритесь и превзмогайте. Было весело.

    Раз руки из одного места, одолжите комп у младшей сестренки с ВыНдОй и нажмите F5 после того как установите студию.
    Я думаю это для вас будет самый простой вариант.

    как можно ж править, это ж идеальный код :)))

    Лекция про Tries, что оно такое и зачем:

    www.youtube.com/watch?v=TJ8SkcUSdbU

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

    См. тж.: news.ycombinator.com/item?id=2108392 и ссылки в комментариях.

    news.ycombinator.com/item?id=2108392

    Хорошая ссылка. И по ссылке вроде даже более-менее взрослый код.
    Но автор утверждает что удалось обойти всего на 50%-100% std::map, этого мало конечно.
    Ну и тесты хотелось бы увидеть.

    ептыть...
    Str keys[1];

    .............
    gen_random(keys[i].Val, KEY_LEN — 1);

    и че, реально ето на винде работает?

    В чём отличие от наивной реализации trie? Хочется на досуге поковырять, но код весьма слабо читаем.

    Это читать не нужно. Там определенно есть UB, т.о. вообще весь код/алгоритм/тест под сомнением. Пусть сначала UB чинит, а потом тесты повторить и обсуждать.

    goto FILL_KEY;

    А вот там куча такого, что пропускает инициализацию и объявление переменных... компилер не ест такое. Если уж в стиле С, то все переменные должны быть в шапке ф.

    Блин, я даже сразу не скажу как это исправить, чтоб не обвинили меня в поломке мега алгоритма. И так уже попереименовал, когда имя типа = имени переменной :)

    Интересно бы собрать это все с тестом std::map на GCC под arm-v6, arm-v7, arm-64, win-lin 32/64 и посмотреть, а то какие-то сомнения меня гложут, уж не под визуалку ли все подточено...да еще 32 бита...
    Кстати, std::map у микрософта свой, а у GCC свой. В стандарте только интерфейс и юзкейсы поведения описаны. Не факт, что будет такой же тормоз на 14с.
    ...и О3 попробовать, а то там присваивания в 8 бит значений 32 бит ...мож так и нада, а мож оптимазер поломает в этом месте.

    #define uint unsigned int

    нууу... arm-thumb режим (по умолчанию обычно, для мелких вещей) — будет 16 бит тут, точно так хотели? пока ставлю using uint = uint32_t;

    #define uint unsigned int
    кстати да, лучше через typedef, на случай если придётся подключить ещё какие-то хедеры определяющие uint.

    Собственно на С++ использование #define это моветон. Почти всегда, есть аналог, который провериться компилером без side-effects.

    нууу... arm-thumb режим (по умолчанию обычно, для мелких вещей) — будет 16 бит тут, точно так хотели?

    Что-то я очень сильно в этом (16 бит int для thumb) сомневаюсь. Пруфы есть?
    А то
    1) между thumb и native переключение влёгкую, в отличие от 16/32/64 на x86
    2) находятся обратные утверждения (например, тут один спутал точно так же с разрядностью команд, и его поправляли)

    Thumb 16 разрядные инструкции, thmub2 mix 16 и 32 разрядных. Ширина данных всегда одинаковая, регистры всегда 32 битные

    Спасибо. То есть я правильно догадался, что Захарова проглючило.

    Ну перефирийный код может быть улучшен.
    Сам алгоритм (в частности функция insert, getValueByKey и тд) не рефакторится не переписываться не будет. Они так написаны, потому что так надо.

    “Так надо” должны говорить тесты на корректность и на скорость. Там наверняка много можно улучшить.

    For the optimisation effort to yield measurable improvements on representative tasks, one must know whether and where effort is needed. A well-informed optimisation depends on sufficiently realistic, detailed and accurate measurements, developers are notoriously bad at guessing where optimisation is necessary.
    twiki.cern.ch/.../WorkBookOptimizeYourCode

    В getValueByKey просится вот такая структура: paste.ee/p/kVHeq

    Пока ты не опубликуешь статью по своему супероптимизированному алгоритму, всем будет наплевать.

    И на статью тоже будет всем наплевать :)

    Да не предложит же.

    На одного перельмана — миллион бубнов. Статистика вещь упрямая. А упоротость автора ни разу не повышает шансов проявить себя.

    теперь, после целого дня срача, ты понял что топикстартер туп, необразован, имеет зашкаливающее чсв, не терпит критику по делу?...
    С таким маиндсетом в принципе невозможно сделать прорывных вещей.
    сравнение его с перельманом — оскорбление для перельмана :)

    Вотето у «архитектора» бомбит. Зачот.

    опять свои фантазии другим людям приписываешь? Ну придумай что нить пооригинальней, шоле.

    На одного перельмана — миллион бубнов и сто миллионов прокофьевых.

    Фиксед, во имя правды.

    ...во имя чесания своего немеряно раздутого чсв :)

    А кто такие сыроеды?
    Гребцы явно гребут на галерах.

    Проблема с Trie в том, что на сегодня очень мало качественных и сбалансированых реализаций. В интернете много опубликовано интересных статей на тему Trie, но это примитивные и узкоспециализированые реализации в основном. Красивые мат теории которые разбиваются о железо на котором приходится выполнять этот самый код.

    А статью я конечно напишу, но тут скорей чтобы люди поняли как на самом деле оно работает.
    Из кода это действительно понять очень сложно даже с дебагером в руках, поскольку оно написано чуть ли не единственным верным способом. Здесь Benchmark ставится во главу угла.

    А ключ можно до 512 Бит увеличить? (℅

    Ключ может быть любой. Хоть килобайт.

    Я вообще не шарю в этих ваших материях, тред не читал и все такое. Если в lmdb запилить — быстрее будет? У меня там хештабла на 32 гб, мне бы может пригодилось

    Вполне может быть ощутимый профит.
    Дело в том что параметры хештаблиц на больших обьема «плывут» экспоненциально с коллизиями.
    Но нужно знать конечно условия задачи, как минимум
    Размер ключа, природа ключей (из чего сгенерированы), количество ключей.

    У меня на данный момент на тестовой базе ~208M ключей, но ожидается на пару порядков^2 больше. Ключ — пара SHA3 в байтовом представлении (полагаю оверхед SHA3 будет значительно выше чем поиск по хештабле). Таблица — метрика для пар прообразов произвольных размеров.

    Я практически полностью уверен что прирост производительности был бы существенным.
    Но дело в том что сейчас у меня есть реализация только для 32х битных систем. Моему процессу ОСь не отдаст более 2-3 гб памяти. А этого не хватит для 208М ключей :(

    .

    Успіхів! А коммерціалізація не плануєця?

    Да, фильм зачотный. Хорошо отображает многолетнее выпиливание того самого :)
    ru.wikipedia.org/...ki/Самый_быстрый_"Индиан

    Блин, ребята, взял себе литрушку пива :)
    Короче хронология событий. Поспал я наверно сегодня часа четыре, просыпаюсь, какоето чудо заморское из мордорского форума постит мне контрпример, утверждает что его пример работает в 3 раза быстрее чем у меня. Еду на работу, все мысли чтозанах. Приезжаю действительно, контрпример, ключ это строка на 256 рандом букв и цифр, мой алгоритм там где насовывал в 10 раз сливает теперь в 3 раза. Ну думаю всё, п-ц, 5 лет коту под хвост. Судьба Днипры висит на волоске. Еслиб хотябы какаято хештабла так отработала, ну там не сильно жалко. Ибо у нее нет и половины функционала что у меня (сканы по диапазону, по маскам ключей и тд), а тут — какието Red-Black деревья в std::map. Начинаю прозревать. Весь день меня тягают по совещаниям, голова не соображает. В обед два кофе «эспрессо», тут начинаю немножко оживать, иногда дорываюсь до своего чудо кода в перерывах между тасками и снимать метрики дерева со своего алгоритма, вроде более-менее нормально, явных перекосов не обнаружено. Тут начинаются первые звоночки, смотрю по расходу памяти. std::map занимает 250 мегабайт, а у меня 760 мегабайт. WTF?!. Декларировал же что еще по памяти выигрываю, а тут такой провал. И тут совершенно случайно под вечер понимаю, что вот это вот самое:

    std::map<char*, uint> mymap;

    На самом деле только декларирует что работает со строкой. А на самом то деле работает с 4 байтами указателя на эту строку ! Посыпаю голову пеплом в недостаочном знании приблуд STL, Трипл фейспалм. Когда моя структура чесно копирует 256 символов как алюч, это чудо копирует всего 4 байта !

    Переписываю, перемеряю. Все становится на свои места. Липовый тест летит в муссорник, контр примеры снимаются. Допиваю пиво. :-D

    Боже упаси мне превратиться во что-то подобное.)

    Зря, живая наука — это всегда интересно.

    Ну мне нравится именно эта задача тем, что она не требует много набирать кода.
    О ней можно просто думать, даже не часто думать, а просто решать как головоломку в свободное время. Вообще этот алгоритм был реализован еще года 2 назад, просто както нерешался его публиковать.Теперь эту реализацию могут «копать» и другие девелоперы. Единственое, мне еще нужно будет поработать над документацией, чтобы тот кто захочет, смог быстрее вникнуть в то что происходит под капотом. Там есть еще что делать, не реализована эффективно функция Delete. Не реализована многопоточность. И есть целая серия мыслей реализовать вариатор блоков. Но это пока все отложено на потом.

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

    эт точно, особенно прикольно про 4 байта, которые никто не гарантирует на платформах отличных от x86 ;)

    вспоминаются сказки ужасов про недостающий байт

    Я таких ребят как ты называю байтодрочерами. :)
    В свое время чем-то таким в моем вузике страдали преподы, когда заставляли реализовывать какой-то алгоритм и замерять память, время выполнения и тому подобное. Вспомнилось.
    Серьезно, ты если будешь кофе жрать и вместо легкой прокрастинации между тасками за теннистым столом задрачиваться такими алгоритмами к 40 годам будешь ныть, что всё болит. А клиентом психиатра станешь гораздо раньше.

    Я таких ребят как ты называю байтодрочерами

    [sarcasm on]
    На самом деле это часть зеленых технологий. Чем быстрее работают алгоритмы, тем меньше греются транзисторы. Чем меньше греются транзисторы в процессорах, тем меньшее энергопотребление серверов. Чем меньше нужно электричества серверам, тем меньше нужно сжечь угля на ТЭС.
    Profit !
    [sarcasm off]

    Тем не менее, только так можно получить существенный прорыв.

    Чем-то существенным отличается от algs4.cs.princeton.edu/52trie/TrieST.java.html ?

    Примерно как аэроплан и боинг.
    Провал хотябы в выборе языка. Джава для низкоуровнего кода — это плохо.

    Я не про язык спрашивал, а про алгоритм. С тем же успехом можно утверждать, что GPL лицензия для библиотечного кода — это провал.

    Алгоритм там базовый книжный, не увидел сходу Патриции, хотя бы ее нужно прикрутить. Мой алгоритм может быть не совсем очевиден ввиду отсутствия документации, но вот есть игрок в похожей весовой категории. Инженеры из HP писали тимой больше года.
    judy.sourceforge.net
    Если хотите продолжить, хотя бы по диагонали гляньте.

    Попробовал собрать под Linux — заколдобился. Где я винду найду и, главное, зачем?

    и топчет вот эти вот эти ваши самые реализации ассоциативных массивов

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

    У меня есть сборка под Линукс. Я чуть позже выложу.

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

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

    Думаю, с одним только кодом далеко (например, в std либу), зайти не получится — нужно формальное описание алгоритма без привязки к коду, ну или с привязкой к псевдокоду.

    1. Не надо «the most optimized». Просто «Trie implementation in C++», или «Fast Trie implementation». Но лучше без «fast», если нет тестов и легко воспроизводимых benchmark’ов.
    2. Tab for indentation, spaces for alignment. Можно забить, и получить срач на тему spaces-vs-tabs вместо обсуждения кода когда оно пойдёт на Reddit.
    github.com/...ValuesByRange.cpp#L22-L29
    3. Не надо оставлять закомментированные блоки. Если один лишний повод для срача.
    github.com/...dKeyAndValue.cpp#L22-L204
    4. StdAfx.h, windows.h, int _tmain(int, _TCHAR*[]) — wtf? system("pause")?!
    5. printf’ы в C++ коде? После include <iostream>? Ещё один повод для срача.

    6*. Не эксперт в VS, но я бы ожидал там два проекта: собственно библиотеку, и отдельно от неё демку/тест/пример который main.c.
    7*. Зачем в .gitattributes строки с 3-й до конца? Зачем мусор в .gitignore? VS реально столько оставляет в проекте, или это просто копипаст откуда-то?

    8. Обычные Makefile-ы сильно увеличат шансы сборки левыми людьми.
    Соответственно шансы получить отзывы по сути, а не по форматированию.
    Или CMakeLists, и поубирать VS проекты.

    9. Тесты, или что-то хотя бы отдалённо похожее на тесты. Сильно рекомендуется. Очень сильно.

    Спасибо за конструктивный совет.
    Дойдут руки, попробую многое из списка учесть.

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

    Контр пример оказался липой. Жду новых.

    На днях спробую запустити в себе.
    Просто так писав, заради фану, чи якусь конкретну задачу вирішував за допомогою алгоритму?

    Якщо він справді настільки крутий, як ти пишеш, спробуй вийти на людей з комітету по стандартизації С++, аби твій алгоритм внесли в СТЛ.
    isocpp.org/wiki/faq/wg21

    Якщо він справді настільки крутий, як ти пишеш, спробуй вийти на людей з комітету по стандартизації С++, аби твій алгоритм внесли в СТЛ.
    Дада, а еще в спортлото надо обязательно написать :))

    закинь на reddit.com/r/programming
    может хайп поднять удастся- заодно более менее отзывы получишь адекватные

    круто! слушай напиши биндинги для python пожалуйста, чтоб можно было потестить

    Всему сове время. Пока апрувим матан и реализацию :)

    DniproDB поддерживает? /Хотел было пошутить я/
    Но Гитхаб, бессердечная ты ... <_<

    Днипра только на нем и работает, это сердце. Иначе было чтото вроде унылого поделия вроде Монго.

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

    Джобс это Джобс. Вторых таких не будет.

    Берите выше, New Денис Попов.

    А вот хохлизм и хуторянство хорошо бы искоренять. Негоже потомкам Королева и Сикорского свято верить что Украинцы не могут быть инициаторами передовых технологий, хотябы в ИТ.
    Тем более когда вам показывают не просто голую теорию, а готовый рабочий код.
    Да и ввиду примитивности интерфейсов самого ассоциативного массива, «подтасовать» результаты там практически невозможно. Оно или работает или нет.

    а

    Готовлю потихоньку раскат гром и молнии на хабр, но пока потренируемся на Доу

    — це не хохлізм і хуторянство? На москві світ клином зійшовся?

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

    Отож-бо, «по-рускі». Навіщо рівнятись на маргінальні азійські спільноти, чому не reddit, HN? Звичка з колоніальних часів — спілкуватись зі світом лише за посередництвом метрополії (або взагалі вважати, що метрополія — це і є весь світ)?

    Мне кажется автору важнее донести свою мысль и получить фидбек, потому он и прицеливается на все доступные площадки.

    Я бы не назвал хабр маргинальным азиатским сообществом. Там сидят люди со всего СНГ, включая Азию, но не одни азиаты сплошь. Там могут быть свои проблемы, низкие зарплаты, плохое знание английского, но в том айти людей с айтишным или математическим бекграундом реально больше, а именно их фидбек интересно услышать автору.
    Я лично хабр не читаю, но сам иногда туда писал, когда набредал на какое-то решение и было желание поделиться им с обширным русскоязычным сообществом.

    Хабрахабр.ру не-маргінальна, навіть центральна спільнота тільки в рамках «русского міра». Для декого це і є весь світ, тому їх шокує думка, що Москва — не центр Всесвіту.

    но в том айти людей с айтишным или математическим бекграундом реально больше

    Більше ніж де, серед коментаторів на HN? Коли я кілька років тому читав хабр, там в коментарях були всі ті самі сміхуєчки, політота та срачі, що і тут. На HN за моїми враженнями коментують одні математики, соціологи, мікробіологи та ракетні конструктори (ну не зовсім, але близько до того).

    Вам везде Москва мерещится. И Путин. И империя. И колониальное прошлое. Это ваше дело. Только это не имеет отношения к тому, что человеку нужен фидбек на его работу.

    В російському сайті на домені .ru мерещиться москва, справді дивно. Про Путіна це ви дофантазували. А про імперію та колонії я написав тільки тому, що автор сам заговорив про «хохлізм і хуторянство» — як докір тим хто не хоче пишатись новим українським генієм.

    Йому не фідбек потрібен, а щоб його хвалили та захоплювались некомпетенті люди. Це видно з подачі матеріалу: мовляв, дивіться який я геній! код «алгоритму» такий, бо так треба, а якщо не вірите, то нічого не буду пояснювати, ставте собі Windows та Visual Studio і перевіряйте все самі.

    А фильмы в миллион раз этот алгоритм умеет сжимать?

    Алексей Бабушкин является разработчиком уникальной нетеряемой флешки-маркера и супералгоритма сжатия, позволяющего сжать 2Гб в 2Кб. Это он вдохновил?

    Не понимаю о чем ты говоришь.
    Это готовая реализация выложеная на GitHub.
    Качаешь, открываешь VS 2015, запускаешь, меряешь.

    Вообще просьба идиотов сюда не писать. Спасибо.

    Есть у меня и такой дудочник
    blog.pikosec.com/?p=83

    Просто он еще не опубликован. Всему свое время.

    А вот и наш герой, настоящий Adult код.
    Он умудряется вставить 10 миллионов 16 байтных рандом ключей всего за 1.3 секунды.
    Когда ближайшему сопернику по весовой категории std::map реализующий унылые красно-черные деревья для этого нужно целых 14 секунд
    github.com/...y/HArrayVarRAM_insert.cpp

    Trie структуры данных
    Tree имелось ввиду? Глядишь учительница в школе поругает.
    Tree имелось ввиду? Глядишь учительница в школе поругает.

    *double facepalm*
    en.wikipedia.org/wiki/Trie

    Качество галерунов на сыроварнях — оставляет желать лучшего.
    Синиоры не знают базовых алгоритмов, бида бида ...

    trie, also called digital tree and sometimes radix tree or prefix tree Ага, ага из той же статьи. Подловил так подловил.

    Это было не тяжело, учитывая уровень некоторых кадров.

    Хм... хотел сказать вы побиты, но чесно говоря — не совсем уверен в качестве теста, т.к. это интерпретатор и существуют накладные расходы + возможно строки не копирует, а берет указатели... Привожу ниже, с integer ключами вставки в нем занимают 1.03с

    #!/usr/bin/lua

    require ’os’

    local test_size = 10000000

    print(’Generating keys, that will take a while...’)
    local keys = {}
    for i=1, test_size do
    local k = ’’
    for j = 1,16 do
    k = k .. string.char(math.random(32, 126))
    end
    table.insert(keys, k)
    end
    print(’Starting measure insertions...’)

    local s = {}
    local x = os.clock()
    for i=1,test_size do
    s[keys[i]] = i
    end
    print(string.format("elapsed time: %.2f\n", os.clock() - x))

    ------------------------------------------------------------

    Вывод:

    ./test.lua
    Generating keys, that will take a while...
    Starting measure insertions...
    elapsed time: 5.33

    Заметье, языку уже за 10 лет, и соотв. алгоритмам в нем. Никаких std::map.

    Upd:
    переписал цикл в 1 строку
    for i=1,test_size do s[keys[i]] = i end

    ./test.lua
    Generating keys, that will take a while...
    Starting measure insertions...
    elapsed time: 5.06

    В тестах, наверное, немного напутано. Ну давайте начнем с самого простого.

    Привожу ниже, с integer ключами вставки в нем занимают 1.03с

    Если у вас ключ это integer (указатель на память для строки, например),
    то вам нужно использовать HArrayInt
    Вот самый простой тест, вставка 20 млн интегер ключей на процессоре iCore5


    const uint COUNT_KEYS = 20000000;

    void testSeqInt()
    {
    HArrayInt ha;
    ha.init(26);
    clock_t start, finish;
    //INSERT ===========================================
    start = clock();
    for (int i = 0; i != COUNT_KEYS; i++)
    {
    ha.insert(i, i);
    }
    finish = clock();
    printf("Insert: %d msec, ", (finish — start));
    //SEARCH ===========================================
    start = clock();
    for (uint i = 0; i != COUNT_KEYS; i++)
    {
    if (ha.getValueByKey(i) != i)
    printf("Error\n");
    }
    finish = clock();
    printf("Search: %d msec.", (finish — start));
    //ha.print();
    ha.destroy();
    }

    Его вывод:
    Insert: 387 msec, Search: 136 msec.

    Тотже результат для std::map
    Insert: 7063 msec, Search: 2777 msec

    Хорошо, в 3 * 2 раза быстрее, чем на i7 на Lua. Так в чем прикол?) Интерпретатор всего в 5-6 медленнее, чем компилированая задача ваша (и быстрее std::map, что для меня просто шок). При этом интерпретатор еще и учет GC ведет и прочее, т.е., думается мне, если из него именно хеши выковырять — будет сравним с вашим алгоритмом. Вообщем, если интересно — поковыряйте сырцы луа (я как-бы намекаю, намекаю, что судя по времени — вы не первый, возможно внутри луа такой же алгоритм уже давно).

    Хорошо, в 3 * 2 раза быстрее, чем на i7 на Lua.
    Что означает 3*2 ? Мой алгоритм всеголишь в 6 раз быстрее работает на процессоре более старого поколения ?
    Интерпретатор всего в 5-6 медленнее, чем компилированая задача ваша
    Эти цифры именно для этого алгоритма нужно еще апрувнуть. Существующие реализации JIT для .NET и JVM для Java которые в большинстве несложных алгоритмов приближаются к нативным реализациям. К томуже где уверенность что Луа не использует просто биндинги на сишную или ассемблерную реализацию ? По-моему мнению, делать фундаментальные алгоритмы на интерпретируемых языках — это преступление. Потому могли сделать и биндингами.
    (и быстрее std::map, что для меня просто шок)
    А для меня совершенно не шок. Потому что хештаблицы за счет чего могут быстрее работать ? За счет урезаного функционала — там отсутствуют сканы по диапазонам ключей, сканы по маскам ключа, расходы памяти могут быть больше. Worst случай для хештаблицы может выродится вообще в обычный скан по связному списку. Поэтому хештаблицы плохо подходят как основные поисковые алгоритмы для тех же баз данных, в отличии от тех же Tree (std::map) и Trie(HArray).
    При этом интерпретатор еще и учет GC ведет и прочее, т.е.
    У вас есть уверенность что именно в эти 1-2 секунды бенчмарка, в ваш высоконагруженый процесс залез фоновый GC и «украл» у вас кванты процессорного времени ?
    думается мне, если из него именно хеши выковырять — будет сравним с вашим алгоритмом. Вообщем, если интересно — поковыряйте сырцы луа (я как-бы намекаю, намекаю, что судя по времени — вы не первый, возможно внутри луа такой же алгоритм уже давно).
    А мне так не думается. Даже скажу почему. Скорей всего перед создателями стояла задача сделать именно Lua. Вскоре ядро Lua написали и пришло время писать под нее библиотеки. В очередном спринте скраммастер запихнул задачу на 2 недели, сделать хештаблицу и еще с пятерку контейнеров. Умельцы открыли первую попавшуюся книжку и передрали к себе в Lua. Конечно, наверное, при первых же бенчмарках их немного пожурили что реализация никуда не годится. Пришлось потратить еще несколько часов времени с перекурами, чтобы оптимизировать ее аж на 15% в следующем спринте.

    Не...lua — это интерпретируемый язык. Собственно весь язык построен на хеш таблицах, вроде вот цитаты с баша:
    bash.im/quote/441753

    А дальше есть реализация интерпретатора на чистом С. И вот в этом чистом С, для реализации возможности языка, должны быть алгоритмы, и походу оч. эффективные. И да, мастеров там не было. Был 1 изобретатель помоему, так и пишит, минорный апдейт раз в несколько лет.

    Про GC — украсть не украл, но запоминать же нужно? — а это очередные таблицы в С коде — какие-то списки связные и т.д., т.е. атомарная LUA операция вставки в С коде реально вставляется в несколько разных таблиц, типа GC таблиц.

    и быстрее std::map, что для меня просто шок
    Это зря

    можно ускорить формирование ключей:

    local x = os.clock()
    local k = {}
    for i=1,test_size do
    	for j = 1,16 do
    		k[j] = math.random(32, 126)
    	end
    	table.insert(keys, string.char(table.unpack(k)))
    end
    print(string.format("elapsed time: %.2f\n", os.clock() - x))
    
    string.char — за цикл, склеивание — одним вызовом (без промежуточных мусорных строк).

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

    А зачем его бить на части ?
    Это фундаментальный алгоритм, он может позволить себе быть упоротым настолько, насколько это только возможно — ради быстродействия.

    ну чтобы его сапортить и развивать было легче а также чтобы другой человек(или даже ты сам через пару лет) сумел быстро разобраться. Собственно говоря это единственная причина декомпозиции софта, о которой столько много умных книг написано и много слов умными же людьми сказано. Заметь, в твоем случае можно декомпозировать легко без потери в перфомансе.

    Зачем ? На тойже джаве сейчас функциониует больше 1 миллиарда устройств.
    Представь что в фундаментальный код, внесут какието правки, чтобы этот оптимизированый код «лучше читался и сапортился» несколькими програмистами. В итоге ты получишь 5% присест по производительности на 1 миллиарде устройств. Стоит оно этого ? Нет.
    Обычно дело обстоит так. Если ты там не можешь разобраться, то ты не можешь там ничего улучшить. Если не можешь прочитать код ядра линукса, нечего туда лезть и улучшать его читаемость. Там и без тебя количество багов стремится к нулю.

    Да не получишь ты просадки по перфомансу в 5%, если канечна специально не задаться целью просадить перфоманс. И да, правки в «фундаментальный код» что джавы, что винды, что линукса, что дотнета вносятся постоянно. В том числе и для улучшения читаемости. Банально все упирается в цену вопроса: цена сапорта говнокода Х, цена сапорта нормального кода X/10.

    У тебя каша в голове. Ты как архитектор должен понимать разницу между сапортом ядра системы и сапортом всего того зоопарка что напишут поверх ядра.
    Ядро, это ядро. Его задача работать быстро, Всё. Менять развивать и улучшать его будут в крайне редких случаях и потратят месяцы работы чтобы изменить буквально несколько строчек. А потом еще эти несколько строчек покроют обьемами тестов значительно больших чем сам код. Весь остальной код, более высокоуровневый, можешь улучшать сколько хочешь. Функиональные обязаности и требования к их оформлению у разных лееров совершенно разные.

    Ты таки упоротый окончательно.
    Вот тебе сорцы ядра линука — ядреней ядра не придумаешь. github.com/...valds/linux/pulse/monthly
    постоянно в него коммитят. Каждый день фактически.
    Вот тебе рандомно открый файл user.c
    github.com/...blob/master/kernel/user.c
    Отлично структурированный код, функции по 20 строк кода. Сам файл в 200 строк.
    Вот второй рандомно открытый файл: github.com/...b/master/kernel/kthread.c
    опять отлично структурированный, читаемый и поддерживаемый код.

    Угу, именно поэтому по признанию самого Линуса Торвальдса, каждая следующая версия Линукс работает на 5-10% медленее чем преведущая.

    Давай лучше приближенней пример возьмем. Когда последний раз вносились изменения в алгоритм работы boost::hash<int,std::string>
    Точно в этом году ?

    Давай лучше приближенней пример возьмем. Когда последний раз вносились изменения в алгоритм работы boost::hash<int,std::string>
    реализаций std более одной. Можешь поискать репозитарий той, которая больше нравится и посмотреть на историю изменений равно как и на его стиль кода.

    Вот как раз пишу развернутые бенчмарки, на std::map<int,int>
    Кусок результатов из консоли


    === HArrayInt VS std::map<int,int> testing ===
    Insert/Search 1000000 SEQUENCE keys (4 bytes each) ..
    HArrayInt => Insert: 22 msec, Search: 8 msec.
    std::map => Insert: 344 msec, Search: 125 msec.

    Insert/Search 3000000 SEQUENCE keys (4 bytes each) ..
    HArrayInt => Insert: 63 msec, Search: 28 msec.
    std::map => Insert: 948 msec, Search: 398 msec.

    Insert/Search 5000000 SEQUENCE keys (4 bytes each) ..
    HArrayInt => Insert: 109 msec, Search: 45 msec.
    std::map => Insert: 1673 msec, Search: 649 msec.

    Insert/Search 7000000 SEQUENCE keys (4 bytes each) ..
    HArrayInt => Insert: 142 msec, Search: 66 msec.
    std::map => Insert: 2286 msec, Search: 896 msec.

    Insert/Search 9000000 SEQUENCE keys (4 bytes each) ..
    HArrayInt => Insert: 184 msec, Search: 80 msec.
    std::map => Insert: 3275 msec, Search: 1191 msec.

    Insert/Search 1000000 RANDOM keys (4 bytes each) ...
    HArrayInt => Insert: 13 msec, Search: 8 msec.
    std::map => Insert: 269 msec, Search: 269 msec.

    Insert/Search 3000000 RANDOM keys (4 bytes each) ...
    HArrayInt => Insert: 39 msec, Search: 39 msec.
    std::map => Insert: 774 msec, Search: 800 msec.

    Insert/Search 5000000 RANDOM keys (4 bytes each) ...
    HArrayInt => Insert: 62 msec, Search: 53 msec.
    std::map => Insert: 1304 msec, Search: 1358 msec.

    Insert/Search 7000000 RANDOM keys (4 bytes each) ...
    HArrayInt => Insert: 102 msec, Search: 98 msec.
    std::map => Insert: 2055 msec, Search: 1993 msec.

    Insert/Search 9000000 RANDOM keys (4 bytes each) ...
    HArrayInt => Insert: 139 msec, Search: 117 msec.
    std::map => Insert: 2580 msec, Search: 2351 msec.

    Insert/Search 1000000 PERIOD keys (4 bytes each) ...
    HArrayInt => Insert: 39 msec, Search: 9 msec.
    std::map => Insert: 309 msec, Search: 113 msec.

    Insert/Search 3000000 PERIOD keys (4 bytes each) ...
    HArrayInt => Insert: 92 msec, Search: 24 msec.
    std::map => Insert: 957 msec, Search: 370 msec.

    Insert/Search 5000000 PERIOD keys (4 bytes each) ...
    HArrayInt => Insert: 153 msec, Search: 56 msec.
    std::map => Insert: 1620 msec, Search: 629 msec.

    Insert/Search 7000000 PERIOD keys (4 bytes each) ...
    HArrayInt => Insert: 233 msec, Search: 72 msec.
    std::map => Insert: 2206 msec, Search: 888 msec.

    Insert/Search 9000000 PERIOD keys (4 bytes each) ...
    HArrayInt => Insert: 289 msec, Search: 93 msec.
    std::map => Insert: 2899 msec, Search: 1167 msec.

    Press any key to continue . . .

    Ты знаешь. После такого не хочется смотреть нинаодну реализацию из std:

    Ты же сам предложил посмотреть на сорцы std.
    А теперь суешь какие говнотесты и говоришь что смотреть не хочешь. Ты вообще хоть немного вменяем?

    Угу, именно поэтому по признанию самого Линуса Торвальдса, каждая следующая версия Линукс работает на 5-10% медленее чем преведущая.
    Урлы на слова Линуса канечна же не будет, я все правильно понял?

    Я это давно читал, нужно поискать где именно.
    А что, мои слова кажутся нелогичными ?

    Твои слова являются тупым балабольством.

    Опять ты сорвался на батхерт.
    Свободен.

    опять приписываешь свои фантазии другим людям, балабол?

    Вот тебе рандомно открый файл user.c
    github.com/...blob/master/kernel/user.c

    © Copyright 1991-2000 Linus Torvalds
    Судя по заголовку, последний раз в этот файл вносили изменениня 16 лет назад.

    Так и запишем: гитом пользоваться не умеешь.
    github.com/...mits/master/kernel/user.c
    Судя по хистори он изменялся менее 2х лет назад.

    Всеголишь 2 года назад ? Ну надоже.
    А что менялось, ты хоть глянул ?
    Весьма забавный трехкопеечный код поменялся. Точней не менялся, а добавили еще один флажок. За 2 года.

    еще раз по буквам: в ядро линукса коммитят фактически ежедневно. Пруф смотри выше. Твой тезис о «ядре которое никто никогда не трогает» — балабольство.

    Підписатись на коментарі