Search
Write a publication
Pull to refresh
285
2.6
Send message

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

Как вы без зума видите эти мелкие шрифты?

В среднем я сижу где‑то на расстоянии 70см от центрального экрана, вполне себе получается читать текст, надписи в софте и всякие имена файлов в проводнике и сайты в 100% масштабе.

Всякие 3д‑максы, Visual Studio и After Effectsы для комфортной работы требуют очень много места на экране, а 200% масштаб его тотально сжирает

Телевизоров 8K уже давно много в продаже, в том числе и 55 дюймов. Sony BRAVIA 8 K-55XR80 как пример.

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

Я поиграл. Очередь быстро проходит, у меня была 52к человек, через 5 минут зашёл. По ощущениям — это и почти «тот самый BF3» только современный.

Мясо, темп и атмосфера больше всего напоминает бф3, нежели бф4. И, на удивление, он хорошо оптимизирован — играл 11 520×2160 было 30–40 фпс на ультрах.

Сглаживание включено, шрифты стандартные, масштаб 100%, и мне норм.

Если нужна чёткость, можно просто поставить масштаб 200% и сесть подальше, но тогда на одном экране будет места как на обычном FullHD мониторе.

Если не нужно 120Гц+, можно просто юзать 8K панели. Мне 120 Гц критично, ибо шутеры, а если только работать с текстом , то 8К 60 Гц вполне норм.

Регуляция как раз штука удобная, а вот работа стоя, имхо — штука сомнительная. У самого подъёмный стол, но использую его для регуляции и как подъёмник, когда надо в системном блоке ковыряться. А стоя за ним не работаю.

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

Нумпад нужен только тем кто его активно юзает, остальным он только место занимает. Имхо, лучшее решение — отдельный нумпад СПРАВА от мыши.

Так и есть, просто люди еще не свыклись с мыслью, что монитор может быть размером с телик. И то, что многие современные телики (не все) можно использовать в качестве мониторов.

А думаете стационарный отключать не будут? :)

Хм. У меня наоборот получается, что немного выигрывает:

| Method                               | Mean              | Error           | StdDev          |
|------------------------------------- |------------------:|----------------:|----------------:|
| LEN_12_only_digits                   |          1.652 ns |       0.0026 ns |       0.0023 ns |
| LEN_12_NOT_only_digits               |          1.093 ns |       0.0019 ns |       0.0015 ns |
| LEN_256_000_000_only_digits          | 10,282,478.846 ns |  60,604.1205 ns |  50,607.1854 ns |
| LEN_256_000_000_NOT_only_digits      |  9,564,862.960 ns | 180,272.5571 ns | 185,126.6438 ns |
| SPAN_LEN_12_only_digits              |          1.656 ns |       0.0049 ns |       0.0046 ns |
| SPAN_LEN_12_NOT_only_digits          |          1.897 ns |       0.0014 ns |       0.0012 ns |
| SPAN_LEN_256_000_000_only_digits     | 10,340,051.146 ns | 181,664.4755 ns | 169,929.0632 ns |
| SPAN_LEN_256_000_000_NOT_only_digits |  9,199,522.098 ns |  90,575.2054 ns |  80,292.5604 ns |
Код
 public static bool IsAllDigitsSpan([NotNull] string str) =>
   str.AsSpan().IndexOfAnyExceptInRange('0', '9') == -1;
 public static bool IsAllDigits([NotNull] string str)
 {
     unsafe
     {
         fixed (char* begin = str)
         {
             char* ptr = begin;
             var charsInVector = Vector<ushort>.Count;
             if (str.Length >= charsInVector) //Если строка достаточно длинная для векторов
             {
                 //Вычисляем конец чтобы поместилось целое число векторов
                 char* endVector = begin + str.Length / charsInVector * charsInVector;

                 var min = new Vector<ushort>((ushort)'0'); //Вектор состоящий из '0'
                 var max = new Vector<ushort>((ushort)'9'); //Вектор состоящий из '9'

                 //Идея простая: берем пачки по N символов
                 //загоняем в диапазон от '0' до '9'
                 //и смотрим есть ли изменения - если есть, значит это были не цифры
                 while (ptr < endVector)
                 {
                     var v = Vector.Load((ushort*)ptr); //Загружаем 16 символов в регистр 
                     var vClamped = Vector.ClampNative(v, min, max); //Загоняем в диапазон от '0' до '9'

                     if (!Vector.EqualsAll(v, vClamped)) //Если что-то изменилось то там были не только цифры
                         return false;

                     ptr += charsInVector; //Идем дальше
                 }
             }
             char* end = begin + str.Length;

             //Если осталось хотя бы 8 символов - добиваем 128-битной инструкцией
             if (end - ptr >= Vector128<ushort>.Count)
             {
                 var min128 = Vector128.Create((ushort)'0');
                 var max128 = Vector128.Create((ushort)'9');
                 var v = Vector128.Load<ushort>((ushort*)ptr);
                 if (!Vector128.EqualsAll(Vector128.Clamp(v, min128, max128), v))
                     return false;
                 else
                     ptr += Vector128<ushort>.Count;
             }

             //Далее анролл по 4
             char* end4 = begin + (str.Length & ~3);
             while (ptr < end4)
             {
                 if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                 if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                 if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                 if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
             }

             //Потом обычным циклом
             while (ptr < end)
             {
                 if (unchecked((uint)(*ptr - '0')) > 9u)
                     return false;
                 ptr++;
             }
         }
     }
     return true;
 }
   [Benchmark] public bool LEN_12_only_digits() => TurboIsDigitChecker.IsAllDigits(small_only_digits);
   [Benchmark] public bool LEN_12_NOT_only_digits() => TurboIsDigitChecker.IsAllDigits(small);
   [Benchmark] public bool LEN_256_000_000_only_digits() => TurboIsDigitChecker.IsAllDigits(big_only_digits);
   [Benchmark] public bool LEN_256_000_000_NOT_only_digits() => TurboIsDigitChecker.IsAllDigits(big);

   [Benchmark] public bool SPAN_LEN_12_only_digits() => TurboIsDigitChecker.IsAllDigitsSpan(small_only_digits);
   [Benchmark] public bool SPAN_LEN_12_NOT_only_digits() => TurboIsDigitChecker.IsAllDigitsSpan(small);
   [Benchmark] public bool SPAN_LEN_256_000_000_only_digits() => TurboIsDigitChecker.IsAllDigitsSpan(big_only_digits);
   [Benchmark] public bool SPAN_LEN_256_000_000_NOT_only_digits() => TurboIsDigitChecker.IsAllDigitsSpan(big);

Предположу, что дело в особенностях реализации SIMD на конкретном железе, размерах кэша, скорости ОЗУ и прочих штуках, поэтому разные подходы дают разные результаты на разном железе. И Spanовская реализация, судя по всему, написана примерно так же, но оказалась лучше заточена под Ваше железо, и хуже под моё.

UPD: вариант, заточенный под мелкие строки:

| Method                       | Mean      | Error     | StdDev    |
|----------------------------- |----------:|----------:|----------:|
| LEN_12_only_digits_SMALL     | 1.4944 ns | 0.0083 ns | 0.0077 ns |
| LEN_12_NOT_only_digits_SMALL | 0.9210 ns | 0.0088 ns | 0.0083 ns |
| LEN_12_only_digits_SPAN      | 1.7246 ns | 0.0053 ns | 0.0047 ns |
| LEN_12_NOT_only_digits_SPAN  | 1.8502 ns | 0.0058 ns | 0.0055 ns |
Код
   public static bool IsAllDigitsSpan([NotNull] string str) =>
            str.AsSpan().IndexOfAnyExceptInRange('0', '9') == -1;
   public static bool IsAllDigits_FOR_SMALL([NotNull] string str)
   {
       unsafe
       {
           fixed (char* begin = str)
           {
               char* ptr = begin;
               char* end = begin + str.Length;

               //8 символов
               if (end - ptr >= Vector128<ushort>.Count)
               {
                   var min128 = Vector128.Create((ushort)'0');
                   var max128 = Vector128.Create((ushort)'9');
                   var v = Vector128.Load<ushort>((ushort*)ptr);
                   if (!Vector128.EqualsAll(Vector128.Clamp(v, min128, max128), v))
                       return false;
                   else
                       ptr += Vector128<ushort>.Count;
               }

               //4 символа
               char* end4 = begin + (str.Length & ~3);
               if (ptr < end4)
               {
                   if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                   if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                   if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                   if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
               }

               //Добиваем обычным циклом
               while (ptr < end)
               {
                   if (unchecked((uint)(*ptr - '0')) > 9u)
                       return false;
                   ptr++;
               }
           }
       }
       return true;
   }
  [Benchmark] public bool LEN_12_only_digits_SMALL() => TurboIsDigitChecker.IsAllDigits_FOR_SMALL(small_only_digits);
  [Benchmark] public bool LEN_12_NOT_only_digits_SMALL() => TurboIsDigitChecker.IsAllDigits_FOR_SMALL(small);
  [Benchmark] public bool LEN_12_only_digits_SPAN() => TurboIsDigitChecker.IsAllDigitsSpan(small_only_digits);
  [Benchmark] public bool LEN_12_NOT_only_digits_SPAN() => TurboIsDigitChecker.IsAllDigitsSpan(small);

Для интереса попробовал на интринсиках (.NET9/Release):

| Method                             | Mean              | Error          | StdDev         |
|----------------------------------- |------------------:|---------------:|---------------:|
| LEN_12_only_digits                 |          1.668 ns |      0.0088 ns |      0.0083 ns |
| LEN_12_NOT_only_digits             |          1.099 ns |      0.0062 ns |      0.0055 ns |
| LEN_256_000_000_only_digits        | 10,137,419.231 ns | 77,538.9046 ns | 64,748.4971 ns |
| LEN_256_000_000_NOT_only_digits    |  9,201,213.951 ns | 92,295.2075 ns | 81,817.2974 ns |
  • Строки в 12 символов за 1.1 — 1.7 нс

  • Строки в 256 миллионов символов — 10–11 мс

public static bool IsAllDigits([NotNull] string str)
{
    unsafe
    {
        fixed (char* begin = str)
        {
            char* ptr = begin;
            var charsInVector = Vector<ushort>.Count;
            if (str.Length >= charsInVector) //Если строка достаточно длинная для векторов
            {
                //Вычисляем конец чтобы поместилось целое число векторов
                char* endVector = begin + str.Length / charsInVector * charsInVector;

                var min = new Vector<ushort>((ushort)'0'); //Вектор состоящий из '0'
                var max = new Vector<ushort>((ushort)'9'); //Вектор состоящий из '9'

                //Идея простая: берем пачки по N символов
                //загоняем в диапазон от '0' до '9'
                //и смотрим есть ли изменения - если есть, значит это были не цифры
                while (ptr < endVector)
                {
                    var v = Vector.Load((ushort*)ptr); //Загружаем 16 символов в регистр 
                    var vClamped = Vector.ClampNative(v, min, max); //Загоняем в диапазон от '0' до '9'

                    if (!Vector.EqualsAll(v, vClamped)) //Если что-то изменилось то там были не только цифры
                        return false;

                    ptr += charsInVector; //Идем дальше
                }
            }
            char* end = begin + str.Length;

            //Если осталось хотя бы 8 символов - добиваем 128-битной инструкцией
            if (end - ptr >= Vector128<ushort>.Count)
            {
                var min128 = Vector128.Create((ushort)'0');
                var max128 = Vector128.Create((ushort)'9');
                var v = Vector128.Load<ushort>((ushort*)ptr);
                if (!Vector128.EqualsAll(Vector128.Clamp(v, min128, max128), v))
                    return false;
                else
                    ptr += Vector128<ushort>.Count;
            }
           
            //Далее анролл по 4
            char* end4 = begin + (str.Length & ~3);
            while (ptr < end4)
            {
                if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
            }

            //Потом обычным циклом
            while (ptr < end)
            {
                if (unchecked((uint)(*ptr - '0')) > 9u)
                    return false;
                ptr++;
            }
        }
    }
    return true;
}
Использование AVX512 прироста не дало
| Method                             | Mean              | Error          | StdDev         |
|----------------------------------- |------------------:|---------------:|---------------:|
| LEN_12_only_digits512              |          1.676 ns |      0.0078 ns |      0.0073 ns |
| LEN_12_NOT_only_digits512          |          1.101 ns |      0.0063 ns |      0.0059 ns |
| LEN_256_000_000_only_digits512     | 10,133,206.250 ns | 77,341.0857 ns | 68,560.8580 ns |
| LEN_256_000_000_NOT_only_digits512 |  9,085,707.091 ns | 53,953.1981 ns | 45,053.3640 ns |
  public static bool IsAllDigits512([NotNull] string str)
  {
      unsafe
      {
          fixed (char* begin = str)
          {
              char* ptr = begin;
              var charsInVector512 = Vector512<ushort>.Count;
              //Основная тушка с векторами
              if (str.Length >= charsInVector512) //Если строка достаточно длинная для векторов
              {
                  //Вычисляем конец чтобы поместилось целое число векторов
                  char* endVector512 = begin + str.Length / charsInVector512 * charsInVector512;

                  var min = Vector512.Create((ushort)'0'); //Вектор состоящий из '0'
                  var max = Vector512.Create((ushort)'9'); //Вектор состоящий из '9'

                  //Идея простая: берем пачки по N символов
                  //загоняем в диапазон от '0' до '9'
                  //и смотрим есть ли изменения - если есть, значит это были не цифры
                  while (ptr < endVector512)
                  {
                      var v = Vector512.Load((ushort*)ptr); //Загружаем 16 символов в регистр 
                      var vClamped = Vector512.ClampNative(v, min, max); //Загоняем в диапазон от '0' до '9'

                      if (!Vector512.EqualsAll(v, vClamped)) //Если что-то изменилось то там были не только цифры
                          return false;

                      ptr += charsInVector512; //Идем дальше
                  }
              }

              char* end = begin + str.Length;

              //Если осталось хотя бы 8 символов - добиваем 128-битной инструкцией
              if (end - ptr >= Vector128<ushort>.Count)
              {
                  var min128 = Vector128.Create((ushort)'0');
                  var max128 = Vector128.Create((ushort)'9');
                  var v = Vector128.Load<ushort>((ushort*)ptr);
                  if (!Vector128.EqualsAll(Vector128.Clamp(v, min128, max128), v))
                      return false;
                  else
                      ptr += Vector128<ushort>.Count;
              }

              //Далее анролл по 4
              char* end4 = begin + (str.Length & ~3);
              while (ptr < end4)
              {
                  if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                  if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                  if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
                  if (unchecked((uint)(*ptr - '0')) > 9u) return false; ptr++;
              }

              //Потом обычным циклом
              while (ptr < end)
              {
                  if (unchecked((uint)(*ptr - '0')) > 9u)
                      return false;
                  ptr++;
              }
          }
      }
      return true;
  }
Код бенчмарка
 public class Benchmark
 {
     static int randomIndex = 0;
     static string generateBigRandomString(bool onlyDigits, int count)
     {
         if (count < 1024)
             throw new NotSupportedException();
         var rnd = new Random(Interlocked.Increment(ref randomIndex));
         var b = new StringBuilder(count);
         var beginNoDigits = count / 10 * 9;
         for (int i = 0; i < count; i++)
             b.Append((char)rnd.Next('0' + 0, '9' + 1));

         if (!onlyDigits)
         {
             for (int i = 0; i < 10; i++)
             {
                 var index = rnd.Next(beginNoDigits, count);
                 b[index] = (char)rnd.Next('a' + 0, 'z' + 1);
             }
         }
         return b.ToString();
     }
     string small_only_digits, small;
     string big_only_digits, big;
     [GlobalSetup]
     public void Setup()
     {
         small_only_digits = "123456732890";
         small = "123456732a90";
         big_only_digits = generateBigRandomString(true, 256_000_000);
         big = generateBigRandomString(false, 256_000_000);
     }

     [Benchmark] public bool LEN_12_only_digits() => TurboIsDigitChecker.IsAllDigits(small_only_digits);
     [Benchmark] public bool LEN_12_NOT_only_digits() => TurboIsDigitChecker.IsAllDigits(small);
     [Benchmark] public bool LEN_256_000_000_only_digits() => TurboIsDigitChecker.IsAllDigits(big_only_digits);
     [Benchmark] public bool LEN_256_000_000_NOT_only_digits() => TurboIsDigitChecker.IsAllDigits(big);

     [Benchmark] public bool LEN_12_only_digits512() => TurboIsDigitChecker.IsAllDigits512(small_only_digits);
     [Benchmark] public bool LEN_12_NOT_only_digits512() => TurboIsDigitChecker.IsAllDigits512(small);
     [Benchmark] public bool LEN_256_000_000_only_digits512() => TurboIsDigitChecker.IsAllDigits512(big_only_digits);
     [Benchmark] public bool LEN_256_000_000_NOT_only_digits512() => TurboIsDigitChecker.IsAllDigits512(big);
 }

Дальше можно в зависимости от фактического размера данных затачиваться:

  • Под короткие строки — тогда выкидываем тушу с векторами и делаем ступенчатую обработку (по типу блока с Vector128, который под тушкой векторов), то есть сначала один раз без цикла проверяем 512, потом 256, потом 128 бит, потом анролл, потом обычный цикл

  • Под длинные строки — тогда можно перед тушкой с векторами поставить проверку по 1 цифре, которая подравняет указатель памяти до кратности размеру вектора, и далее, в тушке векторов, делать не Load, а LoadAligned, что немного быстрее. Ну и можно поиграться со способом проверки — не Clamp/Equals, а какие‑нибудь хитрые трюки с масками

В одну систему ставить топовый игровой проц и самый лоуенд по графике мало кто будет. Нереалистичная пара проц+графика абсолютно. Исследование сферического коня в вакууме.

Задумка ИМХО в том, чтобы исключить влияние процессора на результаты тестов. То есть мы строим систему, где заведомо видеокарта будет бутылочным горлышком.

Ради интереса грузил Qwen3 на 480b параметров с квантизацией Q3. Поручил писать алгоритм изменения яркости в духе

void DoBrightness(Bitmap batman, float multipler)

Попросил всё люто‑бешено оптимизировать с unsafe и LUT таблицами, и чтобы большие битмапы через Parallel. Пахало 12 часов, справилось, честно говоря, на троечку. Видимо квантизация помешала. Модельки на 32b и 72b справились примерно так же, зато ответ выдавали через 10–30 сек.

Выводов не будет, просто делюсь опытом.

LM Studio кстати тоже умеет поднимать сервер, даже несколько моделей параллельно можно грузить.

Что‑то многовато новостей про сокращения в важных инфраструктурных организациях там (Intel машет ручкой). Интересно, к чему это приведёт

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

1
23 ...

Information

Rating
49-th
Registered
Activity