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


Тест WhetStone отличается в выгодную сторону от CoreMark характером проводимых в нем вычислений: все входящие в него тесты, за исключением теста скорости условных переходов, в той или иной степени обладают внутренним параллелизмом. Тестирование производилось в двух вариантах. В первом, — Multiclet R1, — производилась компиляция актуальной версией компилятора LLVM с опциями:

-ffast-math -fno-builtin -O3

Во втором варианте, — Multiclet R1*, — тестирование производилось с внедрением в «ручном режиме» перспективных оптимизаций, добавляемых в настоящее время к компилятору. Ручная доработка сводилась к помощи компилятору в увеличении линейных участков методом объединения нескольких итераций цикла.

Результаты

Система MHz MWIPS/MHz MFLOPS1/ MHz MFLOPS2/ MHz MFLOPS3/ MHz COS MOPS/ MHz EXP MOPS/ MHz FIXPT MOPS/ MHz IF MOPS/ MHz EQUAL MOPS/ MHz
Multiclet R1* 100 0.721 0.256 0.212 0.162 0.018 0.008 3.569 0.417 1.57
RPi 2 v7-A7 1000 0.585 0.28 0.291 0.248 0.011 0.006 1.314 1.209 0.981
RPi 3 v8-A53 1200 0.604 0.276 0.29 0.248 0.01 0.007 1.267 1.561 1.014
ARM v8-A53 1300 0.642 0.268 0.241 0.239 0.028 0.004 1.197 1.436 0.439
Core i7 4820K 3900 0.887 0.341 0.308 0.167 0.023 0.014 0.998 1.504 0.251
Core i7 1 CP 3066 0.873 0.325 0.295 0.174 0.025 0.013 0.892 0.958 0.167
Phenom II 3000 0.799 0.307 0.27 0.111 0.026 0.016 0.835 1.001 0.167
Athlon 64 2211 0.785 0.308 0.272 0.104 0.026 0.016 0.832 0.999 0.187
Turion 64 M 1900 0.884 0.302 0.258 0.145 0.026 0.016 0.827 0.988 0.187
Core i5 2467M 2300 0.853 0.296 0.298 0.163 0.022 0.013 0.807 0.993 0.222
Core 2 Duo 1 CP 2400 0.885 0.337 0.307 0.198 0.024 0.012 0.804 0.81 0.176
Celeron C2 M 2000 0.868 0.297 0.296 0.194 0.023 0.012 0.778 0.781 0.172
Core 2 Duo M 1830 0.878 0.337 0.305 0.197 0.024 0.012 0.751 0.785 0.174
Multiclet R1 100 0.311 0.157 0.153 0.029 0.018 0.008 0.714 0.081 0.143
Celeron M 1295 0.832 0.324 0.297 0.178 0.022 0.012 0.631 0.923 0.173
Raspberry Pi 1000 0.391 0.137 0.146 0.123 0.009 0.004 0.617 1.014 0.805
Athlon XP 2088 0.856 0.307 0.274 0.139 0.026 0.016 0.576 0.998 0.166
Pentium Pro 200 0.79 0.332 0.278 0.146 0.023 0.013 0.575 0.755 0.149
Athlon4 Barton 1800 0.846 0.305 0.272 0.137 0.026 0.016 0.571 0.988 0.165
Celeron A 450 0.76 0.291 0.276 0.14 0.022 0.012 0.569 0.751 0.147
Pentium 4E 3000 0.39 0.182 0.164 0.058 0.014 0.006 0.323 0.27 0.126
Atom M 1600 0.348 0.176 0.157 0.051 0.01 0.007 0.252 0.744 0.11
Pentium 4 1900 0.383 0.214 0.188 0.056 0.012 0.006 0.241 0.427 0.118
Pentium MMX 200 0.615 0.328 0.267 0.079 0.025 0.013 0.198 0.73 0.186
Pentium 100 0.604 0.322 0.267 0.078 0.025 0.013 0.192 0.568 0.183
80486DX2 66 0.182 0.076 0.068 0.026 0.008 0.005 0.105 0.212 0.017
*Вариант использования перспективных оптимизаций компилятора LLVM

Видно, что по показателю MWIPS/MHz мультиклеты смотрятся гораздо увереннее, чем по показателю CoreMark/MHz (цифры опубликованы ранее в статье). Мы можем заметить следующее:

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


Тесты, входящие в WhetStone, можно условно разделить на четыре группы. В первую группу входят тесты на производительность вычислений с плавающей точкой. Результаты этих тестов определяют показатели MFLOPS1, MFLOPS2, MFLOPS3. Видно, что применение дополнительных оптимизаций компилятора LLVM дает существенное ускорение во всех трёх тестах.

Рассмотрим природу получаемого ускорения на примере показателя MFLOPS1 и исследуем, за счет чего достигается такой результат, на примере первого теста арифметики с плавающей запятой. Код теста на Си:

timea = dtime(); 
	 { 
	    for (ix=0; ix<xtra; ix++) 
	      { 
		for(i=0; i<n1*n1mult; i+=5) 
		  { 
		      e1[0] = (e1[0] + e1[1] + e1[2] - e1[3]) * t; 
		      e1[1] = (e1[0] + e1[1] - e1[2] + e1[3]) * t; 
		      e1[2] = (e1[0] - e1[1] + e1[2] + e1[3]) * t; 
		      e1[3] = (-e1[0] + e1[1] + e1[2] + e1[3]) * t; 
		  } 
		t = 1.0 - t; 
	      } 
	    t =  t0;                    
	 } 
timeb = dtime();

При компиляции теста существующей версией LLVM мы получим следующий ассемблерный код для тела внутреннего цикла:

jmp LBB2_4 
	SR4	:=	rdq #IR7, 2160 
	SR5	:=	rdq #IR7, 2152 
	SR6	:=	rdq #IR7, 2144 
	SR7	:=	rdq #IR7, 2136 
	SR8	:=	rdq #IR7, 2128 
	SR9	:=	rdq #IR7, 2120 
	SR10:=	subf @SR6, @SR5 
	SR11:=	subf @SR5, @SR6 
	SR12:=	addf @SR5, @SR6 
	SR5	:=	addf @SR10, @SR4 
	SR10:=	addf @SR11, @SR4 
	SR4	:=	addf @SR10, @SR7 
	SR7	:=	mulf @SR4, @SR8 
	SR10:=	addf @SR5, @SR7 
	SR5	:=	subf @SR4, @SR10 
	SR11:=	subf @SR10, @SR4 
	SR4	:=	mulf @SR10, @SR8 
	SR10:=	mulf @SR5, @SR8 
	SR5	:=	addf @SR12, @SR10 
	SR10:=	addf @SR11, @SR5 
	SR11:=	mulf @SR5, @SR8 
	SR5	:=	mulf @SR10, @SR8 
	SR10:=	addf @SR5, @SR6 
	SR5	:=	mulf @SR10, @SR8 
			wrq @SR9, #IR7, 2760 
			wrq @SR5, #IR7, 2752 
			wrq @SR11, #IR7, 2744 
			wrq @SR4, #IR7, 2736 
			wrq @SR7, #IR7, 2728

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

После процедуры объединения итераций, тело цикла примет следующий вид
jmp LBB2_4
	SR4	:=	rdq #IR7, 272 
	SR5	:=	rdq #IR7, 264 
	SR6	:=	rdq #IR7, 256 
	SR7	:=	rdq #IR7, 248 
	SR8	:=	rdq #IR7, 320 
	SR9	:=	rdq #IR7, 240 
	SR10	:=	rdq #IR7, 232 
	SR11	:=	addf @SR4, @SR5 
	SR5	:=	addf @SR7, @SR6 
	SR12	:=	addf @SR11, @SR6 
	SR13	:=	subf @SR12, @SR7 
	SR12	:=	mulf @SR13, @SR8 
	SR14	:=	addf @SR12, @SR4 
	SR4	:=	addf @SR14, @SR11 
	SR11	:=	subf @SR14, @SR6 
	SR6	:=	addf @SR11, @SR7 
	SR11	:=	subf @SR13, @SR6 
	SR12	:=	subf @SR6, @SR13 
	SR13	:=	mulf @SR11, @SR8 
	SR11	:=	addf @SR5, @SR13 
	SR5	:=	addf @SR12, @SR11 
	SR12	:=	addf @SR11, @SR4 
	SR13	:=	mulf @SR5, @SR8 
	SR5	:=	mulf @SR12, @SR8 
	SR12	:=	addf @SR13, @SR7 
	SR7	:=	mulf @SR12, 0x3f000000 
	SR12	:=	subf @SR5, @SR7 
	SR5	:=	addf @SR12, @SR6 
	SR6	:=	addf @SR12, @SR11 
	SR13	:=	subf @SR5, @SR11 
	SR11	:=	addf @SR5, @SR4 
	SR4	:=	mulf @SR13, @SR8 
	SR5	:=	mulf @SR11, @SR9 
	SR11	:=	addf @SR4, @SR7 
	SR4	:=	subf @SR11, @SR12 
	SR12	:=	subf @SR6, @SR11 
	SR6	:=	mulf @SR12, @SR8 
	SR11	:=	subf @SR13, @SR12 
	SR12	:=	addf @SR6, @SR7 
	SR6	:=	mulf @SR8, @SR11 
	SR11	:=	addf @SR4, @SR12 
	SR4	:=	mulf @SR12, @SR8 
	SR13	:=	mulf @SR11, @SR8 
	SR11	:=	addf @SR4, @SR5 
	SR4	:=	addf @SR13, @SR7 
	SR5	:=	mulf @SR4, 0x3f000000 
	SR4	:=	subf @SR11, @SR5 
	SR7	:=	addf @SR4, @SR12 
	SR12	:=	addf @SR6, @SR4 
	SR6	:=	mulf @SR12, @SR8 
	SR12	:=	addf @SR6, @SR5 
	SR13	:=	addf @SR6, @SR11 
	SR6	:=	subf @SR12, @SR4 
	SR4	:=	subf @SR7, @SR12 
	SR7	:=	mulf @SR4, @SR8 
	SR4	:=	addf @SR7, @SR5 
	SR7	:=	addf @SR6, @SR4 
	SR6	:=	addf @SR4, @SR13 
	SR11	:=	mulf @SR7, @SR8 
	SR7	:=	mulf @SR6, @SR8 
	SR6	:=	addf @SR11, @SR5 
	SR5	:=	mulf @SR6, 0x3f000000 
	SR6	:=	subf @SR7, @SR5 
	SR7	:=	addf @SR6, @SR12 
	SR11	:=	addf @SR6, @SR4 
	SR12	:=	subf @SR7, @SR4 
	SR4	:=	addf @SR7, @SR13 
	SR8	:=	moveq @SR8 
	SR9	:=	moveq @SR9 
	SR10	:=	moveq @SR10 
	SR7	:=	mulf @SR12, @SR8 
	SR13	:=	mulf @SR4, @SR9 
	SR4	:=	addf @SR7, @SR5 
	SR7	:=	subf @SR4, @SR6 
	SR6	:=	subf @SR11, @SR4 
	SR4	:=	mulf @SR6, @SR8 
	SR9	:=	subf @SR12, @SR6 
	SR6	:=	addf @SR4, @SR5 
	SR4	:=	mulf @SR8, @SR9 
	SR9	:=	addf @SR7, @SR6 
	SR7	:=	mulf @SR6, @SR8 
	SR11	:=	mulf @SR9, @SR8 
	SR9	:=	addf @SR7, @SR13 
	SR7	:=	addf @SR11, @SR5 
	SR5	:=	mulf @SR7, 0x3f000000 
	SR7	:=	subf @SR9, @SR5 
	SR11	:=	addf @SR7, @SR6 
	SR6	:=	addf @SR4, @SR7 
	SR4	:=	mulf @SR6, @SR8 
	SR12	:=	addf @SR4, @SR5 
	SR13	:=	addf @SR4, @SR9 
	SR4	:=	subf @SR12, @SR7 
	SR7	:=	subf @SR11, @SR12 
	SR9	:=	mulf @SR7, @SR8 
	SR11	:=	subf @SR6, @SR7 
	SR6	:=	addf @SR9, @SR5 
	SR7	:=	mulf @SR8, @SR11 
	SR9	:=	addf @SR4, @SR6 
	SR4	:=	addf @SR13, @SR6 
	SR11	:=	mulf @SR9, @SR8 
	SR9	:=	mulf @SR4, @SR8 
	SR4	:=	addf @SR11, @SR5 
	SR5	:=	mulf @SR4, 0x3f000000 
	SR4	:=	subf @SR9, @SR5 
	SR9	:=	addf @SR7, @SR4 
	SR7	:=	addf @SR4, @SR6 
	SR6	:=	mulf @SR4, @SR8 
	SR11	:=	mulf @SR9, @SR8 
	SR9	:=	addf @SR11, @SR5 
	SR11	:=	subf @SR7, @SR9 
	SR7	:=	subf @SR9, @SR4 
	SR4	:=	mulf @SR9, @SR8 
	SR9	:=	mulf @SR11, @SR8 
	SR11	:=	addf @SR9, @SR5 
	SR9	:=	addf @SR7, @SR11 
	SR7	:=	mulf @SR11, @SR8 
	SR11	:=	mulf @SR9, @SR8 
	SR8	:=	addf @SR11, @SR5 
	SR5	:=	mulf @SR8, 0x3f000000 
			wrq @SR10, #IR7, 384 
			wrq @SR6, #IR7, 376 
			wrq @SR4, #IR7, 368 
			wrq @SR7, #IR7, 360 
			wrq @SR5, #IR7, 352

Вторая группа тестов оценивает скорость работы основных математических функций и характеризуется показателями COS MOPS и EQUAL MOPS. Оптимизации компилятора не оказывают заметного влияния на показатели данных тестов, поскольку основная нагрузка ложится на математическую библиотеку. Существенное негативное влияние на результат этих тестов оказал факт того, что используемая математическая библиотека писалась под старый процессор P1 и не использовала многие преимущества более нового процессора R1.

В третью группу тестов можно объединить тесты производительности целочисленной арифметики (показатель FIXPT MOPS) и производительности работы с массивами (показатель EQUAL MOPS). На тесты данной группы влияют все процессы, повышающие производительность в первой группе, кроме того, увеличенные линейные участки, полученные путем объединения итераций цикла, могут быть оптимизированы стандартными средствами оптимизации компилятора LLVM. Эти оптимизации существенно сокращают число необходимых промежуточных вычислений и приводят к тому, что итоговые показатели тестов для R1 оказываются в 1,5...2 раза выше аналогичных показателей Intel и ARM.

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

Таким образом, текущая ревизия процессора, при достаточной длине линейного участка и достаточном количестве взаимонезависимых команд внутри него, обеспечивает скорости исполнения, сопоставимые с актуальными версиями ядер семейства ARM и Intel. Неплохие результаты достигаются для показателей MFLOPS1, MFLOPS2, MFLOPS3. Превосходные результаты по показателям FIXPT MOPS и EQUAL MOPS связаны не только с особенностями работы мультиклеточной архитектуры, но и с результатами компиляторных оптимизаций алгоритма, производимых на увеличенных линейных участках, что приводит к некоторому завышению результатов в этом тесте за счет сокращения числа выполняемых действий. Не слишком хорошие показатели COS MOPS и EXP MOPS определяются недостаточным вниманием к оптимизации математической библиотеки и будут улучшены в будущем.

Что касается самого компилятора, то с момента написания прошлой статьи его функционал для мультиклеточной архитектуры был заметно расширен:

  1. Добавлена поддержка 64-х разрядной целочисленной арифметики.
  2. Добавлена возможность генерирования отладочной информации.
  3. Добавлена цель (опция -target), обеспечивающая генерацию ассемблерного кода только с использованием вещественной арифметики одинарной точности (типы double, long double имеют размер 32 бита, как тип float).
  4. Добавлены опции компилятора, обеспечивающие использование только 32-х разрядных инструкций записи (необходимость в этом возникла из-за особенности реализации внешней памяти процессора R1, в которую можно писать только 32-х разрядные значения).
  5. Оптимально реализованы библиотечные функции memset(), memcpy(), memmov().
  6. Были проведены исследования возможности поддержки компилятором векторных инструкций, результаты которых не выявили необходимости в реализации данной возможности из-за ограниченности набора векторных инструкций, поддерживаемых самим процессором R1.

В целом компилятор LLVM обновлён до версии 3.8.1.
Поделиться с друзьями
-->

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


  1. VioletGiraffe
    11.08.2016 10:31
    +7

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


    1. GenadyIvanovich
      12.08.2016 08:13

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


      1. VaalKIA
        12.08.2016 14:17

        Совершенно согласен, что для разных топонорм «на МГц» это более правильный показатель, однако не поленился проверить вот что:
        http://www.ixbt.com/cpu/pentium4-2.html
        Последний проц от интел по топонорме 180Нм, и максимум частоты, которой они достигли:
        Intel Pentium IV Willamette

        0,18 микрон, которым располагает Intel, не в состоянии работать на частотах, существенно превышающих 1 гигагерц. Предел данного технологического процесса находится где-то в районе 1,1–1,2 ГГц — именно поэтому, из-за недостаточной стабильности работы в предельных режимах, и был отозван опрометчиво выброшенный на рынок Pentium III Coppermine 1,13 ГГц.

        а тут про Виламет
        Согласно заявлениям Intel, процессоры, основанные на данной технологии, позволяют добиться увеличения частоты примерно на 40 процентов относительно семейства P6 при одинаковом технологическом процессе. Соответственно, для технологии производства .18 микрон предел частоты составляет примерно 1,55 — 1,7 ГГц — вполне конкурентноспособный ход для обеспечения достойного отпора компании AMD, продолжающей наращивать тактовые частоты своих медных Thunderbird'ов.

        В процессоре Willamette ALU (Arithmetic Logic Unit) — арифметико-логические блоки (устройства) — работают на удвоенной частоте процессора, например, у процессора с частотой 1,4 ГГц АЛУ будет работать на частоте 2,8 ГГц, т.е.

        В итоге последний Виламет был 2ГГц.
        Так что вопрос имеет место быть, почему всего 200МГц?


        1. AKudinov
          12.08.2016 14:29

          Отвечу вопросом на вопрос: ARM7TDMI рассчитан на 130нм, почему же у его реализаций тактовая частота выше 100МГц не поднималась?
          Там уже включается более «низкоуровневая» оптимизация: у P4 конвейер содержал около 30 ступеней (ещё и пресловутый replay был), соответственно, каждая ступень была короче; но и цена промаха больше. Плюс к тому, имела место ручная оптимизация.


          1. VaalKIA
            12.08.2016 14:32

            По большей части — не знаю, но могу предположить, что армы всегда были заточенны на энергопотребление и высокими частотами не должны обладать, хотя, я думаю там разница была бы процентов 30%, а не 2000% (пусть даже архитектурная оптимизация +оптимизирван сам техпроцесс)


          1. VaalKIA
            12.08.2016 14:41

            Ещё могу предположить, что у арма и интела жутко сильно отличаются количество тактов на инструкцию, но не думаю что то что арм делает за 10 тактов, интел будет делать за 2000, так что всё ещё не знаю.


        1. GenadyIvanovich
          15.08.2016 09:49

          Допустимая тактовая частота процессора, при заданной топонорме, определяется количеством блоков логических элементов на линии. Число блоков может быть уменьшено:
          1) Конвееризацией вычислений.
          2) Оптимизациями на этапе синтеза.
          3) Применением специальных «быстрых» библиотек при синтезе.

          Поскольку первые два пункта требуют больших трудозатрат, при разработке процессора R1 вопросы оптимизаций на этапе синтеза не поднимались (все было синтезировано стандартным САПРовским синтезатором, что не очень эффективно), а оптимизировались и конвееризировались только те линии, длинна которых не позволяла выйти на частоту 100 МГц.

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


          1. VaalKIA
            15.08.2016 22:09

            В данном случае интересует сердце мультиклета, то, что связывает клетки, при 64 клетках на 180 Нм оно позволит достичь 1 ГГц?


            1. GenadyIvanovich
              16.08.2016 15:21

              Мегагерцы не зависят от числа клеток, и может оказаться, что, повышая частоту, потеряем производительность, особенно на определенных задачах. Для достижения на 180 нм частоты Интела, нужно отказаться от ASIC и вручную делать топологию, что крайне ресурсоемко и далеко не всегда оправданно. Кроме того, на современных техпроцессах (порядка 20 нм) результаты ASIC сопоставимы с ручной разработкой.


  1. Sergei2405
    12.08.2016 08:41

    Микросхему переверните, а то работать не будет
    image


    1. AKudinov
      12.08.2016 09:12

      Зависит от конструкции корпуса, если точнее, от того, сверху или снизу к выводам приваривается керамическая поддерживающая рамка. У корпуса с КДПВ рамка приварена снизу, поэтому в спутник микросхема ставится «лицом» вверх.
      У корпуса на вашем фото рамка, очевидно, приварена сверху, и микросхему иначе, как «лицом» вниз, не поставить.


      1. Sergei2405
        12.08.2016 09:37

        Это корпус 4245.240-6
        image
        И его поставили наоборот только ради красивой фотки…


        1. AKudinov
          12.08.2016 09:53

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


          1. Sergei2405
            12.08.2016 10:19

            Да, признаю, в это контактирующее корпус вставляют лицом вверх.
            image


            1. AKudinov
              12.08.2016 10:31

              На самом деле, от корпуса сильно зависит. Вот у меня рядом другой корпус лежит:


              Он в это же КУ лицом вниз ставится, иначе рамка мешает.


  1. VaalKIA
    12.08.2016 14:27

    В статье про тесты всё очень хорошо описано и почти все вопросы, которые хотел задать, отпали сами собой, но:
    Не понятно чем всё же отличаются FLOPS1-3
    Первый раз слышу про MWIPS и даже после вики, не очень понял чем это отличается от MIPS:

    MWIPS (Mega Whetstone Instructions Per Second). В известном смысле указанные единицы аналогичны MIPS, но с одной существенной оговоркой: Whetstone-инструкции не привязаны к системе команд какого-либо компьютера, т. е. оценка производительности в MWIPS является моделенезависимой

    Каждый модуль теста выполняется многократно, в соответствии с исходной статистикой Whetstone-инструкций (практически это реализуется с помощью заключения модулей в циклические конструкции с разным числом «оборотов» цикла — от 12 до 899), а производительность рассчитывается как отношение числа Whetstone-инстpукций к суммарному времени выполнения всех модулей пакета.

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