1. Куча / Говнокод #17668

    +126

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    // На самом деле это пока только псевдокод. Real code will be on C/C++
    //Lock-free non blocking and anatomic operation only if IP was changed 
    // IN SHARED MEMORY
    non_atomic_in_shmem bad_IP_flag_non_atomic_in_shmem[64][16]; // in real code it will be uint64_t array[16] and bits operations with it
    some_ip_class IPs_non_atomic_in_shmem[64][16]                   //  
    std::atomic<uint64_t> version_holder_atomic_in_shmem[64][16];   // it is debatable "whether to do it the Atomic" but for reinsurance 
    
    // IN WRITER PROCESS
    void worker_reassign_IP_by_num(new_IP, back_end_server_ID, num)
     if  bad_IP_flag_non_atomic_in_shmem[back_end_server_ID][num]   // in real code this check will be not here but will have same sense
        version_holder_atomic_in_shmem[back_end_server_ID][num] ++ //it is increment of atomic var version and version become NOT even
        __sync_synchronize(); //TODO may be we can use something better here ?
        IPs_non_atomic_in_shmem[back_end_server_ID][num] = new_IP   //it  copy assignment
        __sync_synchronize();  //TODO may be we can use something better here ?
        version_holder_atomic_in_shmem[back_end_server_ID][num] ++ //it is increment of atomic var version and version become even
        __sync_synchronize();  //TODO may be we can use something better here ?
        bad_IP_flag_non_atomic_in_shmem[back_end_server_ID][num] = 0
    
    // IN READER PROCESS
    some_ip_class get_valid_ip_by_num(back_end_server_ID,num){
    //TODO  version_holder is atomic for now, may we do it non atomic? I think YES. Are any arguments in opposites?
    // Instead of atomic can we possible use volatile?
        uint64_t version_before = version_holder_atomic_in_shmem[back_end_server_ID][num] //it is atomic copy assignment
        //if (version_befor & 1)    // is not even  it will work too instead of checking flags
        // but checking flags it is less expensive operation than checking version because the version var is atomic 
         if  bad_IP_flag_non_atomic_in_shmem  //
                return 0;
         
        some_ip_class ip = IPs_non_atomic_in_shmem[back_end_server_ID][num]     
        uint64_t version_after = version_holder_atomic_in_shmem[back_end_server_ID][num] // it is atomic copy assignment
    
        if  version_before != version_after  // versions is not sames
                // we may add extra check of version evenest but it will cost us 1 slow atomic operation and excessively
    //OR  (version_after & 1) //or versions is not even ip is not correct
                return 0    
        return ip
    }
    some_ip_class get_valid_ip(back_end_server_ID)
        while(time_not_expaired) {
            for (n=0; n<16; n++){
                   some_ip_class ip = get_valid_ip_by_num(back_end_server_ID, n)
                   if ip 
                           return ip
            }
         //"it will never happened" "if it happened and time expired do something but it is problems on the back end servers site"
        //”it is similar situation with situation when back end server just down ”     
        }
        return 0 // or some server_down_ip ;)
    
    some_ip_class check_or_get_new_ip( curent_ip, back_end_server_ID,  num)
        if NOT bad_IP_flag_non_atomic_in_shmem
                    if current_ip == shmem_array_of_IP_non_atomic[back_end_server_ID][num] // maybe need copy but I think it's not necessary
                return current_ip

    Задача: есть reader(ы) Это процессы 20 - 30 -64, которые обращаются к web-серверу. У web-сервера может быть несколько ip (не больше 16). Переодически (1 раз в ~30 секунд) пара (1-2) IP может отвалиться и вместо них может появится пара 1-2 новых. reader обнаружив bad IP выставляет флаг bad_IP_flag. Writer (только 1 единственный процесс не reader) Переодически (1 раз в ~1 сек) проверяет флаг, увидев bad_IP_flag запускает DNS-lookup который длиться примерно (30mks - 1s). Обновляет массив ip Readers при попытке соединиться проверяют не стал ли IP плохим если стал пробуют другой из массива Но суть не в этом Суть в том как безопасно работать с shared memory? Где что не так в предложенной задумке?

    Запостил: apgurman, 20 Февраля 2015

    Комментарии (147) RSS

    • Переписать на Go.
      Ответить
    • q
      Ответить
    • Переписать на WCT!
      Ответить
    • >it will never happened
      >it is problems
      Hello, me call Boris, ai talk inglish very best.
      Ответить
      • > it copy assignment
        > versions is not even
        > Instead of atomic can we possible use volatile?
        Ответить
      • >It is debatable but for reinsurance
        Ответить
      • Спасибо за то, что указали на ошибки. Это реальный текст который учавствовал в обсуждении просблммы. И в компании никто кроме меня не говорит по русски. Но вот эти буржуи настолько воспитаны, что ни разу не поправили И как-то суки все поняли. Ну Вам конечно спасибо.
        Ответить
        • > по русски
          > Переодически
          > вместо них может появится
          > DNS-lookup который длиться

          у меня плохие новости.
          Ответить
      • Да он и в русский очень хорошо получаться.
        Ответить
    • > Суть в том как безопасно работать с shared memory?
      Полно инфы в интернете. Можно и тупо шаред-мутексы для начала брать, lockfree-очереди замутить поверх буста.

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

          Всё с точностью до наоборот.

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

          У вас же 16 ip, которые можно хоть все в один RPC-вызов запихать и RPS смехотворный, да ещё и всё на одной машине.

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

          Сокеты, опять же, гораздо лучше скейлятся, если вдруг надо расположить воркеров на нескольких машинах.
          Ответить
          • Китаец, тоже так говорил. Но это все только в голове На самом деле все теже проблемы есть и в решении с собщениями. и решаются они не проще. Вот что я ответил что всех убедило. Заранее спасибо всем кто поругает мой английский))
            About complexity

            Puzzle for “socket-based” system
            Proxy 1[P1] found IP1 is bad, sent notification message[NM] to worker-process[W]
            W sent broadcast[BR] and start DNS lookup[DN] (or start DN and sent BR?)
            P2 found IP1 is bad before receive BR sent NM to W
            Before receive NM from P2, W already did one more DN (because a Pn reports a IPn is bad) and in this DN W see the IP1 is become a good
            Questions:
            What has to do W with this bad-good IP1 ?
            What has to do W if all 2 or 3 or 4 or N IPs will be bad-good?

            In contrast to the almost unbelievable situation hang-upend for 30s process, this situation is not only quite possible it might even be considered normal for system based on sockets.

            I sure this task can be solved intellectually. Also I'm sure that this puzzle is not easier than the puzzle with the shared memory and I'm also sure this is not the last such a puzzle for the asynchronous, non blocking, socket-based solutions with queues, broadcasts, slow data streams and multiple copies of same data.
            Ответить
            • Так а где паззл-то?
              Крепим к каждому сообщению числовую версию таблицы IP, которую имеет ридер. При каждом апдейте таблицы обновляем версию таблицы на писателе, рассылаем версию вместе с новой таблицей. Так писатель сразу поймёт, какие сообщения от читателей относятся к устаревшим данным.

              Тем более, если реализация с сокетами уже есть и работает, смысла долбиться в шареную память - нуль.
              Ответить
              • Ну это на словах все так просто А ты садь и попробуй все это написать И поймешь что не так все просто Версия говоришь? Версия на таблицу или на IP? Если на таблицу то в разных версиях могут быть одинаково валидные IP если на IP то скорее всего будет очень похоже на то что я делаю в своем варианте В общем здесь все тоже кинулись предлагать сырые идеи которые при рассмотернии оказались далеко не просто сделал В итоге вариант с share mem выглыдел ни чуть не сложнее но точно производительнее и кстати Вас удивит но масштабировать sockets тоже проблемно У меня сейчас русских буковок на клаве нет(( Очень трудно печатать по русски Но через несеолько часов буду дома могу объяснить почему
                Ответить
                • > Если на таблицу то в разных версиях могут быть одинаково валидные IP

                  И что? В чём проблема-то? Пришёл запрос на устаревшую версию - до свидания, обновляйся, проверяй снова.

                  > при бродкасте все процессы а их например может быть 64 должны отложить свои задачи и начать обрабатывать запрос.

                  Они в один поток работают и ждут каждый запрос синхронно?

                  > А он им нифига вообще не нужен, они сидят и работают с сервером и IP который уже 2 года не менялся.

                  Зачем тогда бродкастить? Сломалось - иди узнавай свежую инфу у мастера, ни к чему канал забивать.

                  > здесь все тоже кинулись предлагать сырые идеи

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

                  Ну и у меня стойкое ощущение, что тут происходит попытка завелосипедить уютный сервис координации, вроде ZooKeeper.
                  Ответить
                  • "Зачем тогда бродкастить? Сломалось - иди узнавай свежую инфу у мастера, ни к чему канал забивать."

                    В этом то и проблема Ты провисишь в открытом http/tcp XX-s пока узнаешь что ты висишь на больном IP а уже через 1-2 сек мог бы переползти на новый туда куда твой сервер уполз. Люди потратили кучу денег и мозгочасов чтобы сделать миграцию живой а ты им серпом по ... В общем Вы не хуже меня знаете недостатки такого подхода Поищите лучше проблемы/ошибки в моем алгоритме. Нет там в чистом виде ни мьютексов ни локов и блокировок быть не может. Все работает как часы Быстрее за в 1000 sockets и вопросов в 100 раз меньше Правда чтобы понять где могут проблемы действительно нужно подумать Сложность бывает разная В одном случае голова не может вникнуть в другом случае не может охватить размах Это как бег на короткую или длинную дистанцию Одному проще напрячься на 10 сек Другому лучше дольше но не так интенсивно. ))
                    Ответить
                    • Кстати не ты один провисишь В каждом процессе куча клиентов может пооткрывать коннешин и висеть пока один не обнаружит больной ip а учитывая как долго им будут отвечать они еще и сами захотят об этом сообщить Ты конечно придумешь как и это обойти Но сложность то растет Так что ждет тебя марафонская дистанция с кучей строк кода А через год ты забудешь нафига ты так сложно все это сзделал и стартанешь заново на 40 км В моем случае это 5 ну может быть 20 очень сложных строчек Но их 2 десятка а не 2 десятка тысач если не 2 десятка десятков тысячь ))
                      Ответить
                    • > Все работает как часы Быстрее за в 1000 sockets
                      Откуда инфа? Есть замеры?

                      Расскажи мне лучше, как ты будешь без лока атомарно обновлять версию вместе с содержимым. Или атомарно сеттить адреса IPv6.
                      А потом ещё скажи, как ты без мутекса с кондваром будешь предотвращать 100% СPU на писателе в ожидании бэд-флагов от читателей.

                      Ну и столь нужный бродкаст ты чем собрался заменять.
                      Ответить
                      • "Откуда инфа? Есть замеры?"
                        Есть приблизительная оценка опирающаяся на замеры Почитай я уже это выкладывал Ну и сам прикинь Это полезное качество приближенно считать

                        "Расскажи мне лучше, как ты будешь без лока атомарно обновлять версию вместе с содержимым. Или атомарно сеттить адреса IPv6.
                        А потом ещё скажи, как ты без мутекса с кондваром будешь предотвращать 100% СPU на писателе в ожидании бэд-флагов от читателей."
                        Вот на эту тему я бы и хотел поговорит Если не трудно попытайся понять мою идею Там все подробно изложено если чуть мозг напрячь то в общем-то не так сложно (короткая дистанция))))

                        "Ну и столь нужный бродкаст ты чем собрался заменять."
                        Так у меня он по определению не нужен у меня копия данных то одна И bad_flag один на каждый IP но он один на всех клиентоа Он shared )) Вот почему shared лучше))
                        Ответить
                        • "без мутекса с кондваром будешь предотвращать 100% СPU"
                          Поскольку у меня IP меняется не часто (1 раз в 30 сек пара IP из 16 на сервер) я могу просыпаться раз в 1 сек На семом деле этот процесс делает еще много чего неспешного Это такой периодикал хэлпер (удивляюсь как ты сам то не подумал что можно просто sleep если скучно и нечем занятся)))
                          Ответить
                          • > sleep вместо cond. var
                            Это пиздец, ребята. Зовите санитаров.
                            Ответить
                            • Про sleep это условно Я уже писал что этот поток много чем занят кроме того что делать dns lookup И это не основная его задача По рассчетам он будет проверять bad_flag примерно 1 раз в сек совершенно не критично если это произойдет через 2,3 ..5 сек Сам по себе DNS lookup дело не спешное и по моим замерам от 0.03 до 1.2 s занимает Время которое IP может не отвечать измеряется 10-ками сек. Борманд так что sleep я написал чтобы вот тебе долго вот-это не писать Но ты бля все равно заставил
                              Ответить
                        • > Так у меня он по определению не нужен у меня копия данных то одна

                          Если ридер посчитал адрес валидным, а через микросекунду его пометили битым, ридер будет весь tсp таймаут ждать ответа. А с бродкастом (который придёт быстро) можно отклеится постфактум и попробовать другой адрес.
                          Ну либо повесить в каждый ридер по треду и поллить состояние памяти атомарными ридами (сыкономили процессорное время, ага). В общем, выигрыш не такой очевидный.
                          Ответить
                          • Ты немного путаешь ридеров и клиентов Это наверно моя вина Я не описал это точно в начале. Ридер - это ридер shared-memory это отдельный процесс/программа/proxy внутри которого может быть куча клиентом которые хотят пообщаться с сервером. И каждый раз перед тем как начать обхаться(ЭТО ОЧЕНЬ ЧАСТО) им нужен IP в слкчае с shm я просто промеряю общий для всех процессов/клиентов флаг валидности IP. В слкчае с сокетами как в лучшем(как мы уже договорились) случае копия этого флага для каждого процесса и бродkаст Ты же не собираешься запрашивать валтдность IP при открытии соединения путем создания нового соединения? Ну это конечно шутка Но все равно каждый раз при открытии ты не можешь посылать сообщение Только бродкаст в твоем случае Со всеми его проблемами
                            Ответить
                    • ну я же говноязык , животное, не про баги, ты питух, а про то, что штеуд ты питух, врайтер и создает тебе питушку этот versions is хрень глупая лалка not even
                      маздайка instead of ты питух, atomic can we possible it might заедушный , анскилябра, even be considered as advanced in next 1-2 years, Царь this situation анскильная кукарекалка in case ты питух, of using заедушный питушок sockets ~2000 wider than анскильное говно for shared memory and анскилед i'm also я тут Царь питушня sure thistask can be ко-ко-ко solved intellectually. also заедушный питушок i'm sure this last such маздайка a puzzle with the shared memory?
                      полно инфы в интернете. говноязык , анскилябра, можно и тупо , животное, слился ты животное, шаред-мутексы для начала всегда говно использовать очереди замутить говноязык днище поверх буста.

                      puzzle днище for the тебе питушку asynchronize вроде бы хватит release семантикой , анскилябра, слился ты животное с репликацией, но, конечно, говноязык говно этот подход полон фатальных недостатков, ты кукарекушка, лишь кукарекаешь
                      Ответить
                  • Это исправление "Быстрее в 1000 раз чем sockets"
                    Ответить
              • Вот например представьте при бродкасте все процессы а их например может быть 64 должны отложить свои задачи и начать обрабатывать запрос. А он им нифига вообще не нужен, они сидят и работают с сервером и IP который уже 2 года не менялся. Я где-то им считал во что это обходиться Жить конечно можно Но очень не хорошая ситуация И нельзя это называть "хорошим дизайном"
                Ответить
              • Some calculations

                Now we have 64 servers that can change (we think) 1 IP every 30s. 1 Broadcast every ~ 0.5s.
                With 256 servers, we will be distracted by broadcast every ~0.1s.
                With 1024 (as in Alteon) for every 30ms.

                The same situation if we will figure out the servers change not 1 IP every 30s but 4 or 16. In a future lets call it 64 Floating IPs (64FIPs), 256FIPs, 1024FIPs.

                Lets imaging we are running FV on Xeon-server with 128 cores. And 1 socket transduction is ~50мкs.

                All processors all processors spend together 128x50=6400 mks for every broadcast. 6.4ms to doing something that may be not useful at all.

                One more uncomfortable situation
                All 128 processes just before receiving broadcast decide reports about bad IP by them self in same time And because definition of “same time” for sockets ~2000 wider than for shared memory, this situation in case of using sockets may be periodical and normal.
                In this situation subsystems of SMFHub+worker may be busy to service only ELB up to:
                for 64FIPs – 0.6% of its time
                for 1024FIPs – 20% of its time

                So we see... even for a modern system costs are significant. For systems which will be considered as advanced in next 1-2 years, this approach may not be appropriate at all.
                Certainly all of the calculations are very approximate and they use the maximum values that do not necessarily meet in real systems, but all these parameters and the situation is much more likely than those that interfere to name variants A,B,C as “a good design”.
                Ответить
          • Нужно добавить что предыдущий мной предложенный алгоритм допускал маловероятную ошибку в совсем маловероятном случае: "в случае если читатель подвиснет на 30 сек в момент чтения IPv6 - IPv6 это 16 byte Это 2 x unit64 что трудно сделать атомарной операцией"
            Ответить
          • "У вас же 16 ip, которые можно хоть все в один RPC-вызов запихать и RPS смехотворный, да ещё и всё на одной машине."

            Это я упростил ситуацию 16 на сервер Серверов в одном продукте до 64 в другом до 1024 А что если через пару лет нужно будет 64x1024 ?
            Ответить
    • На x86_64 (речь же явно не про армы или 32-битное говно?) есть cmpxchg16b. И, насколько я помню, 128 битные чтения атомарны при верном выравнивании. Так что если в твоём компиляторе найдётся интринсик для него, то можно сделать так.

      В шареной памяти храним таблицу айпишек. Битые слоты помечаем нулями или ff'ками.

      Ридер атомарно перед каждым запросом (sse'шным интринсиком) читает айпи. Если он, помечен как битый - переходит к другому слоту. Если при коннекте проблема - через cmpxchg16b помечает как битый и отправляет уведомление райтеру (любым способом).

      Райтер просыпается по уведомлениям и через cmpxch16b перезаливает айпишки.

      P.S. Температура под 40, до компа сегодня не доберусь. Так что про атомарность 128-битных чтений пруф или опровержение найти не смогу.
      Ответить
      • Борманд, привет! Очень рад что ты опять мне помогаешь. Про cmpxch16b погуглю конечно сам. Но использовать или нет еще подумаю. Мой алгоритм хорошь тем, что ему пофиг сколько байт длинна собственно данных При этом он не использует локи и если нужно может использовать "специальную атомарность" только когда данные нужно обновлять Что обычно бывает существенно реже чем просто их читать Так что реализация этого алгоритма впринципе полезна. При этом даже если это cmpxch16b, работает так же быстро, как обычный доступ к памяти(что еще не факт), мой алгоритм будет иметь незначительный оверхед, Но зато он менее критичен к разрядности платформы и собственно данных.
        Ответить
        • Ну я идею твоего алгоритма понял - если версия изменилась пока читали данные, то прочитанные данные могут быть битыми и мы пробуем еще разок (в данном случае из следующего слота). В общем-то классика жанра.

          Вместо полного sync_synchronize вроде бы хватит release семантики на инкременте и aquire на чтении в ридере

          64 битный счетчик явно не успеет переполниться если ридер заснул посреди чтения... Других проблем пока не вижу.
          Ответить
          • Except may be situation when Proxy will be hangs in the middle of process of reading IP for ~8.768317965×10¹² years, due to the overflow of the counter the versions will match and in this moment of time worker will screw up IP. By the way if we will change type of variable “version” from uint64_t to uint32_t this event may happened after ~2041.5 years. For uint16_t - ~11.38 days.
            Ответить
          • >>если версия изменилась пока читали данные, то прочитанные данные могут быть битыми и мы пробуем еще разок

            это вообще законно?


            а не проще реализовать простой на время апдейта версии?
            Ответить
            • > это вообще законно
              Вот я над этим сейчас как раз думаю... И не могу найти ничего, что остановило бы компилятор и любой проц от переноса загрузки айпи за вторую проверку версии. Если они эту загрузку туда перенесут - получим успешную проверку версии и битые данные. Причем как в коде ОП'а так и в моём, который ниже.
              Ответить
              • Жопа в общем. То ли я слепой, то ли нет ни одной статьи, говорящей о том, какой параметр надо передать атомику, чтобы он не допускал load-load reordering. Даже про sequential consistent, который юзает ОП, мутно - кто-то пишет, что вообще ничего не реордерится через него, а кто-то, что только чтения справа налево и записи слева направо...

                Завтра, надеюсь, до компа добраться смогу, хоть стандарт почитаю да тесты погоняю...

                Если кто в курсе, что именно гарантирует seq_qst, отпишитесь пожалуйста.
                Ответить
          • > Других проблем пока не вижу.

            Мне вот кажется, что первичная инициализация такой вот памяти при старте системы должна доставить немало проблем.
            Ответить
            • Пометить всё как битое, запустить writer'а, немножко подождать разве не прокатит?

              Скорее всего сам райтер и создает этот расшаренный кусок...

              Ответить
            • Роман когда кожется крестятся Мы здесь должны оперировать словами "знаю" "не знаю" "уверен" "ошибся" А не "верю" "кажется". Аргументы на стол!)) У нас только один писатель и у него кстати будут монопольные права на запись Какие проблемы?
              Ответить
              • > У нас только один писатель и у него кстати будут монопольные права на запись
                Так вроде каждый прокси может срать во флажки, не?
                Ответить
                • Флажки отдельная история Почитай что мы там с Бормандом обсуждаем От них вообще мало что зависит они только для скорости нужны Так что их инициализация точно не проблема
                  Ответить
        • И да. Не делай битовый бед флаг. Он и сейчас немного рискованный - соседи далеко не сразу могут заметить, что его поставили. А с неатомарными битовыми операциями всё переломается к хуям.
          Ответить
          • С какого как ты гооворишь хуя поломается битовый флаг? Я имею ввиду первую его проверку Во втором случае я склонился больше к проверке нечетности И вовсе не из-за атомарности А скорее из-за барьеров(потом объясню как это на барьеры или их отсутствие влияет). Битовый флаг атомарностью или ее отсутствием в моем алгоритме испортить нельзя В конечном варианте (если проверять нечетность) то он нужет только для увеличения производительности. И дженерали его ставят читатели а снимает писатель соответсвенно если пораскинуть хорошенько мозгами то никто там ничего испортить не может даже при отсутствии атомарности Иначе компьютеры бы вообще не работали)))
            Ответить
            • Я про коммент в четвертой строке. Если ты бедфлаги свалишь в кучи по 64 штуки, причем не атомарные. А потом неатомарными операциями будешь их выставлять. То допустим один ридер прочел 64 флага, начал один ставить, в это время второй засунул туда еще флаг, первый закончил. В итоге второй флаг потеряли. Разве нет?

              Насчет рискованности в текущем варианте - ну так я же не про баги, а про то, что врайтер и соседи не сразу увидят изменение.
              Ответить
              • Хотя это "не сразу" совсем недолго - где-нибудь всё равно нарвётся на барьер и сбросит кеши в оперативку.
                Ответить
              • Нужно подумать Возможно ты прав Насчет потери флагов 10 ЖИРНЫХ + тебе Если это так то нужно делать флаги 8 16 32 или даже 64 битными (ну чтоб голову не ломать про портабельность) наверно 32 хороший вариант в моем контексте
                Ответить
              • Зачем читать писать битовые флаги не атомарно. компаре_эксчендж юзхай
                Ответить
                • Оп латенси задрачивает, так что CAS ему покажется лишним...
                  Ответить
                  • Какие-то айпишники сервера и тут блин латенсе на касе жуткий бутельнек. Он поехавший?
                    Ответить
                    • Считай что это "от любви к искусству". Буду юзать uint64_t и uint32_t чтоб вопросов не было
                      Ответить
                      • И после этого к тебе твои каллеги серьезно продолжают относится?
                        Ответить
                        • Ко мне то почему нет? Если наверно даже к тебе кто-то так относится. А ты сам, вообще продолжаешь думать, что к тебе все относятся серьезно.
                          Ответить
          • Соседи просто обязаны его видеть. IP перехал, через ~ 10 -20 s Это увидит первый client/process Выставит флаг Примерно еще через 1-2 s Writer/worker запустит DNS lookup который продлится еще 0.03 - 2 s и только после этого он сможет обновить IP Я кстати склоняюсь не писать новый IP в ту же ячейку А просто писать в "состарившуюся" если повезет и они еще будут и снимать флаг по факту перезаписи Так как-то вернее будет Да и вероянтость поиметь "мусорный" ip меньше. Хотя и теперь ее нет))
            Ответить
          • > И да. Не делай битовый бед флаг. Он и сейчас немного рискованный - соседи далеко не сразу могут заметить, что его поставили. А с неатомарными битовыми операциями всё переломается к хуям.

            Соседи также могут по ошибке снести хороший флаг в плохой при удачном шедулинге. Это как раз та проблема, с которой началась критика передачи сообщений.
            Ответить
            • Роман, мы уже где то здесь(см пару строчек выше) договорились с Бормандом не делать имфлаг битовым А сделать байтным например А лучше словным))
              Ответить
      • "Ридер атомарно перед каждым запросом (sse'шным интринсиком) читает айпи"

        Вот если твое "атомарно" будет по производительности раз в 10 меньше обычного доступа к памяти то я точно это не выберу Я кончно понимаю что скорость выбора IP не сильно повлияет в целом на производительность. Но просто "из любви к искусству" как-то жлобно тратить в 10 раз больше времени на одно и тоже да еще и ограничивать себя разрядностью до 64/128.
        Ответить
        • Должно столько же быть по времени. Ведь на чтение там обычная ссешная инструкция, а cmpxchg только на запись. Тут главная засада как раз в том, атомарно ли это чтение...
          Ответить
          • В моем алгоритме даже атомарность не важна возможно нужны барьеры для writer и в случае смены ip в reader в обоих случаях после чтения version Хотя вот что-то мне говорит что не нужны они там Вот это изначально и хотел пообсуждать Это хорошо что ты борманд в теме))
            Ответить
    • Можно еще объединить бедфлаг, апдейтфлаг и версию. Пишу с кофеварки, так что не ругайся:
      void mark_bad_ip(int n, ver_t ver) {
        if (versions[i].compare_exchange_strong(ver, ver+1, relaxed, relaxed)) {
          // here we can send notification to wr
        }
      }
      
      // reader fragment
      ver_t ver = versions[i].load(aquire);
      if (ver & 1)
        return;
      ip_t ip = ips[i];
      if (versions[i].load(relaxed)!=ver)
        return
      
      // writer fragment
      ver_t ver = versions[i].load(relaxed);
      if (!(ver & 1))
        return;
      ips[i]=new_ip;
      versions[i].fetch_add(1, release);
      Ответить
      • А те еще е пофиг ругаются ли они или нет Главное ведь чтоб мысль была видна А то .... ну в общем ... ты понимаешь .... )))
        Ответить
      • Мысль видна Но не работает это(( Вот здесь:
        mark_bad_ip
        У тебя клиентов/процессов много они тебе там одновременно наинкрементируют Или я мысль не до самого конца увидел?
        Ответить
        • > наинкрементируют
          Там версия перед инкрементом сверяется. Так что не должны.

          Но у этого кода другие косяки из-за релакседов ;(
          Ответить
          • "Там версия перед инкрементом сверяется." тогда еще больше не работает))
            Ответить
            • Почему? Ты про compare_exchange_strong или про что-то другое?
              Ответить
              • Ну вот была у тебя версия там 1000 а стала 10000000 и ты видишь что IP плохой И что? не нужно сообщать о том что он плохой? И вообще как теперь отличить кто плохой а кто Хороший? Или я что-то не понял?
                Ответить
                • Конечно не нужно. Раз в этом слоте того айпи нет, то смысл о нем сообщать? Кто-то уже сделал это за нас.
                  Ответить
                  • А слот то что для тебя?
                    Ответить
                    • Ну одна айпишка одного из серверов. В общем один элемент из твоего IPs_non_atomic_in_shmem.
                      Ответить
                      • Я сейчас на шашлык башлык убегаю но обещаю тебе подумать на эту тему Но пока, чтоб ты не скучал)), есть еще один аргумент не мешать функции флага с функцией версии У меня флаг доступен для записи многим А версия только writer(у) При этоя в принципе запрещаю менять версию кому-либо кроме него А вот у тебя все кому не лень могут туда "насрать" Как сам то думаешь что лучше? Ну а главное какие плюсы то у тебя то есть? Cъэкотомить память проиграв в скорости? Так мег памяти ничего не стоит А вот 10 ns * 100 000 это много))
                        Ответить
                        • Ну я же не знал, что флаг отдельно тестят, чаще чем версию. Считай неправильным пониманием тз.
                          Ответить
                          • я уже извинился за неправильную постановку задачи Был бы ты рядом даже проставился бы))
                            Ответить
                • > И вообще как теперь отличить кто плохой а кто Хороший?
                  Это вообще злободневный вопрос. Для начала всегда считай себя хорошим. А дальше уже всех твоих антагонистов - плохими, а их антагонистов соответственно хорошими и так далее закрашивай граф. Если вершина уже закрашена - сохрани старый цвет и забей на её антагонистов
                  Ответить
                  • Спасибо за совет Но флаг не уберу причины смотри выше.
                    Ответить
                    • Для начала всегда считай себя хорошим. всех твоих антагонистов - плохими
                                                          ^^^^^^^^^^^
                                      флаг не уберу. причины смотри выше.
                      Ответить
                      • Не уберу, и совершенно безотносительно моего отношения к тебе. У меня просто нет никокого личного отношения к тебе. Пока ты не предоставишь, действительно убедительных, аргументов против использования флага, я просто не вижу смысла его убирать. Пока что я считаю, что флаги, пусть и, на твой взгляд, незначительно, но улучшают производительность. Память расходуется при этом не значительно. Это то, что попросил найти выше. Но тебе явно прикольнее и легче пообсирать, чам подумать и доказать обратное.
                        Ответить
                        • Стандарт смотрит на подобные трюки с флагами как на говно: If one evaluation modifies a memory location, and the other reads or modifies the same memory location, and if at least one of the evaluations is not an atomic operation, the behavior of the program is undefined (the program has a data race) unless there exists a happens-before relationship between these two evaluations.

                          Впрочем, это еще никого не останавливало.
                          Ответить
                          • Борманд мне кажется мы пошли по кругу. Это уже 4-раз я пишу Мне этот флаг здесь нужен в 99,999999999% а может быть и чуть больше чтобы не делать медленную проверерку версии Если кто-то что-то там испортит в 0,000000001% случаев это истравит проверка версии И стоить это мне будет 10ns в худшем случае 1 лишний DNS lookup который итак может случиться Вы тут все мне предлагаете каждый раз при запросе IP потратить лишние 10 ns при этом не получив ничего в замен. Главный вопрос ЗАЧЕМ? Стандарт это общий случай у нас частный
                            Ответить
      • И еще я уже писал про это
        Вот этот versions[i].load(aquire) раз в 5-10 медленне чем
        Какой-нибудь if (bad_IP_flag) флаг только и нужен для того чтобы проверять версию когда есть подозрения что все меняется или уже поменялось Что бывает не чаще 1 в 30s А вот за это время ты его обратишься к флагу сотни тысяч раз Посчитай сколько зазря процессор поюзаешь )))
        Ответить
        • aquire load всяко быстрее, чем твой sequential consistent load в 23 строке ;)
          Ответить
          • Борманд вкл ючи мозг Я третий раз пишу 23 строчка вызывается не чаще чем 1 раз в 30 сек. Именно потому что есть флаг (unit32_t например) который я проверяю, а он не атомик и вообще это просто переменная целостность которой меня в общем-то постольку поскольку интересует. А ты версию каждый раз когда тебе нужен IP проверяешь А это может быть 100 000 раз в сек и более Сечешь разницу?
            Ответить
            • Т.е. вся get_valid_ip_by_num ызывается раз в 30 секунд, только когда напоролись на битый порт, а на каждом запросе тестится только бед флаг?
              Ответить
              • Борманд, извини когда изначально копировал недовыделил последнюю строчку

                some_ip_class check_or_get_new_ip( curent_ip, back_end_server_ID, num)
                if NOT bad_IP_flag_non_atomic_in_shmem {
                if current_ip == shmem_array_of_IP_non_atomic[back_end_server_ID][num] // maybe need copy but I think it's not necessary
                {return current_ip}
                }
                return get_valid_ip(back_end_server_ID)

                Как то так я еще скобочки дописал
                Ответить
                • Хотя очень стремно сюда код копировать Или у меня скилзов это делать не хватает))
                  Ответить
                  • Ну есть немного. Главная проблема - его потом ни удалить ни поправить. Так что юзай кнопку предпросмотра.
                    Ответить
                  • Так, если речь о комментарии, то надо писать "[code]Мой код = 1;[/code]" для полного счастья.
                    Мой код = 1;
                    Ответить
                    • А блин, я думал речь о коде в шапке. Тупанул, да.

                      Тащемта ман по ббкодам болтается под редактором общения.
                      Ответить
                      • Борманд, знаешь сколько манов у меня везде и даже на самом видном месте болтается Если я их все буду читать то некогда писать будет Тут все такие умные давно бы написали что нибудь чтобы автоматически это делать или как минимум дать исправить если видишь что криво вышло. Но в целом каюсь Должен был этот ваш bbcode использовать
                        Ответить
                        • без питух мутекса с маздайка кондваром будешь предотвращать 100% хрень сpu
                          поскольку анскильная кукарекалка скилзов это делать питушара анскильная или по определению питух не нужен у меня заедушная копия данных приэтом даже если уже штеуд говно есть отлаженный заедушный собственно данных то одна заедушная питушня для питушков и bad_flag анскилед один на говно каждый запрос синхронно?

                          а говноязык он им заедушный нифига вообще не работает куча и ещё раз повторю, тебе животное: баз данных со сложной семантикой ко-ко-ко и репликацией, нужно может использовать очереди говно сообщений.
                          роман, мы говно уже где ты питух, поскольку интересует. заедушный а ты анскилябра, версию каждый раз говно когда изначально копировать sockets и тебе питушку вопросов в 100 раз, в сек и более сечешь разницу? питушарский т.е. вся get_valid_ip(back_end_server_id][num] ++ //it is increment , of atomic can we питушарский possible it, might заедушный , анскилябра, even be considered as advanced питух in next 1-2 , слился как животное, , питух, иди кукарекай, years, due to ко-ко-ко заедушный the almost unbelievable заедушный situation in case

                          парень пишет я тут Царь хуже меня! fike и ещё раз повторю, тебе животное: заедушный питушок спасибо тебе говно тоже кинулись предлагать сырые питушня для питушков идеи
                          Ответить
                          • Ты думаешь это тот самый скильный непитушняный бот кукареку ассемблерный гуру оптимизатор тех времен?
                            Ответить
                            • А вы явно в одном корыте. Вы нашли друг друга.
                              Ответить
        • Или 23 строка тоже должна быть закомменченной? Хотя как, дальше же юзается этот version_before...

          И проверка в 24 строке была бы халявной - ты же локальную копию смотришь там, а не сам атомик.
          Ответить
          • Вот с тем что "роверка в 24 строке была бы халявной" согласен именно поэтому уберу оттуда проверку флага Тем более что это тоже раз в 30 сек работает
            Ответить
    • Насчет volatile - оно не имеет вообще никакого отношения к потокам. Если, конечно, ты не пишешь непереносимый код под вижуалку 2005+.
      Ответить
      • Я где то читал что volatile гарантирует memory ordering еще не проверял Но в ближайшие дни обязательно займусь)) Но скорее всего это только на этапе компиляции А не на уровне hw То есть в моем writer нужно что-то другое Я думаю в сторону организавать зависимости между переменными тогда вроде как кеши будут гарантировать ордер И это много дешевлее барьеров Если я все правильно понимаю
        Ответить
        • На вижуалке 2005+, насколько помню, работает абсолютно как release/aquire на атомиках. На остальных компиляторах ничего не гарантируется даже со стороны компилера, а тем более железа.

          А почитать для уверенности никогда не помешает.
          Ответить
          • Вот и почитаем)) Для этого говнокод сайт и нужен чтоб знать что читать
            Ответить
            • http://en.cppreference.com/w/cpp/atomic/memory_order

              В самом конце про volatile и треды.
              Ответить
              • Так ты сам то понял? я кажется именно это и имел ввиду когда писал что где-то про это писал.
                Вопрос то простой Можно ли version и/или IP сделать volatile вместо того чтобы юзать барьеры или нет?
                Яркий пример вот это место:
                // IN WRITER PROCESS
                void worker_reassign_IP_by_num(new_IP, back_end_server_ID, num){
                 if  bad_IP_flag_non_atomic_in_shmem[back_end_server_ID] [num] {  // in real code this check will be not here but will have same sense
                    version_holder_atomic_in_shmem[back_end_server_ID][num] ++ //it is increment of atomic var version and version become NOT even
                    __sync_synchronize(); //TODO may be we can use something better here ?
                    IPs_non_atomic_in_shmem[back_end_server_ID][num] = new_IP   //it  copy assignment
                    __sync_synchronize();  //TODO may be we can use something better here ?
                    version_holder_atomic_in_shmem[back_end_server_ID][num] ++ //it is increment of atomic var version and version become even
                    __sync_synchronize();  //TODO may be we can use something better here ?
                    bad_IP_flag_non_atomic_in_shmem[back_end_server_ID][num] = 0
                }
                }
                Ответить
                • Барьеры можешь выкинуть ибо у тебя в каждый инкремент атомика УЖЕ впилен зверский sequential consistent барьер. Только установку бед флага перенеси на строчку выше, до второго инкремента.

                  version нельзя сделать volatile, т.к. тогда ты потеряешь последний атомик, и у тебя даже ++ будет баговать. Попробуй из двух тредов поувеличивать volatile переменную. Удивишься, сколько инкрементов будет проёбано, если, конечно, код собран не вижуалкой 2005+.

                  А ip нет смысла делать volatile. Что ты хочешь этим добиться?
                  Ответить
                  • Задачка:
                    volatile uint64_t version = 0;
                    void thread() {
                        for (uint32_t x = 0; x < 1000000000; ++x)
                            version++;
                    }
                    Один тред увеличивает переменную на миллион. На сколько увеличат ее 4 таких треда на четырехъядерном x86_64?

                    Ответ: хуй знает, у меня выводит что-то в районе 900кк, что меньше 1ккк.
                    Ответить
                    • Чет меня смущает такой ответ. По идее не должно быть меньше 1ккк
                      Ответить
                      • UB'ы, особенно многопоточные, идеям неподвластны...

                        Видимо так: Один тред прочитал переменную и решил немного поспать. Остальные три дрочили-дрочили увеличивали-увеличивали, тот проснулся увеличил старое значение на 1 и продолжил работу.
                        Ответить
                        • на шарпах выдает 1370kk

                          ибо нет всяких убов
                          Ответить
                          • Скукотища у вас там...
                            Ответить
                            • ни какого анального секса...

                              смотрите на видео - 50 убэшек C++
                              Ответить
                  • Что то мне говорит что один из нас не до конца понимает что такое atomic и для чего нужны барьеры. Вот почитай сначала http://bartoszmilewski.com/2008/08/04/multicores-and-publication-safety/ http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/ Ничего личного bormand никак не хотел тебя задеть Это действительно сложно и непоняно Но atomic чтение и запись почти никак не связаны с memory reordering от чего собственно барьеры и спасают.
                    Ответить
                  • Извиняюсь "в каждый инкремент атомика УЖЕ впилен зверский sequential consistent барьер" как то пропустил эту фразу Я уверен ты понимешь что такое барьеры ТОлько я неуверен что УЖЕ впилен
                    Ответить
                  • "установку бед флага перенеси на строчку выше" - вот это тоже лишнее. Ну попадешь ты между флагом и вторым инткриментом и что? Данные валидны, но ты их все равно таковыми посчитать не сможешь, только время потеряешь. А время - деньги, как известно.
                    Ответить
                • Ну и насчёт скорости (4ккк чтений, 4 треда, 4 ядра [email protected], x86_64):
                  std::atomic<uint64_t> b;
                  volatile uint64_t a;
                  uint64_t c;
                  b.load(std::memory_order_seq_cst) // 4.1s
                  b.load(std::memory_order_acquire) // 4.1s
                  b.load(std::memory_order_relaxed) // 4.1s
                  a // 4.1s
                  c // 0s, gcc вынес чтение из цикла, а цикл выкинул
                  В реальных условиях seq_cst отстанет от остальных из-за барьера.

                  Многоядерного ARM'а, к сожалению, нету под рукой. Надо смарт менять.
                  Ответить
                  • Я проверю свои измерения А ты почитай что я послал И подумай головой может ли одинаково быстро выполняться обычное чтение из памяти и atomic и уж тем более код с наличием барьеров?
                    Ответить
                    • >>Борманд, привет! Очень рад что ты опять мне помогаешь.
                      >>Спасибо за совет. И вообще за активное участие. Ты много мне помог.
                      >>А ты почитай что я послал И подумай головой...

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

                          Самый умный макак?

                          >>врспитанный

                          Да не спеши, мы подождем...
                          Ответить
                          • > Да не спеши, мы подождем...
                            Вот это верное замечание. Подобный текст выглядит хуже говнокода.
                            P.S. А вот Борманду - почёт. Так хорошо писать даже с дискового телефона - это удел великих.
                            Ответить
                            • Борманд - маскот говнокода. Точнее - его светлой части

                              Чего еще можно желать?
                              Ответить
                            • Хотя в данном конкретном случае я не могу его осуждать.
                              Ответить
                            • Я уже два дня не с телефона пишу.
                              Ответить
                              • http://memesmix.net/media/created/lsjgu9.jpg
                                Ответить
                              • В том и дело, что разницы не видно без специального комментария "я не с компа".
                                Ответить
                    • На x86_64 - запросто. Это ж не запись и не read-modify-write, которые эксклюзивно захватывают кешлайн. Это всего лишь чтение.

                      Дополнительный полный барьер на этой архитектуре стоит только в seq_qst. А остальные должны быть вообще голыми чтениями (ну т.е. ничем не хуже твоего чтения без атомика).
                      Ответить
                      • ты прав насчет "т.е. ничем не хуже твоего чтения без атомика" Но гарантировано ли вот это без барьеров?
                        uint64_t version_before = version_holder_atomic_in_shmem
                        if (version_befor & 1) 
                        	return 0;
                        some_ip_class ip = IPs_non_atomic_in_shmem;
                        uint64_t version_after = version_holder_atomic_in_shmem
                        if  (version_before != version_after) 
                        	return 0;
                        return ip;

                        мне кажется Да А тебе?
                        Ответить
                        • > uint64_t version_before = version_holder_atomic_in_shmem
                          > uint64_t version_after = version_holder_atomic_in_shmem
                          Вот эти 2 строки в твоём коде на самом деле являются version_holder_atomic_in_shmem.load(std: :memory_order_seq_qst). Так что в этом коде таятся 2 самых сильных барьера (mfence на x86, x86_64).

                          Или ты про то, будет ли этот код работать при более слабых ограничениях?
                          Ответить
                          • В том то и дело, что я сомневаюсь, что они там хранятся. Может ошибка у меня где?
                            ./test
                            static var: 3.01566
                            volatile var: 3.01455
                            static mb var: 3.01095
                            mfence var: 22.1155
                            syn syn var: 22.0879
                            lfence var: 8.86857
                            lfence inc var: 10.1764
                            std::atomic var: 3.01193
                            std::atomic inc var:11.6356
                            Ответить
                          • inline uint64_t static_x_read(){
                                static uint64_t x=0;
                                return x;
                            }
                            inline uint64_t static_mb_x_read(){
                                static uint64_t x=0;
                                asm volatile ("" : : : "memory");
                                return x;
                            }
                            inline uint64_t static_mfance_x_read(){
                                static uint64_t x=0;
                                asm volatile ("mfence" ::: "memory");
                                return x;
                            }
                            inline uint64_t static_syn_syn_x_read(){
                                static uint64_t x=0;
                                __sync_synchronize();
                                //asm volatile ("mfence" ::: "memory")
                                return x;
                            }
                            inline uint64_t static_lfence_x_read(){
                                static uint64_t x=0;
                                asm volatile("lfence" : : : "memory");
                                return x;
                            }
                            inline uint64_t static_lfence_inc_x_read(){
                                static uint64_t x=0;
                                asm volatile("lfence" : : : "memory");
                                return x++;
                            }
                            inline uint64_t volatile_static_x_read(){
                                volatile static uint64_t x=0;
                                return x;
                            }
                            inline uint64_t atomic_x_read(){
                                static std::atomic<uint64_t> x(0);
                                return x;
                            }
                            
                            inline uint64_t atomic_inc_x_read(){
                                static std::atomic<uint64_t> x(0);
                                return x++;
                            }
                            Ответить
                          • #define BILLION 1000*1000*1000
                            #define NUM 1000000000
                            void time_test(uint64_t (*f)(void)){
                                struct timespec tstart = {0,0}, tend = {0,0};
                            
                                clock_gettime(CLOCK_MONOTONIC, &tstart);
                                for (uint64_t i = 0; i < NUM; i++)
                                    f( );
                                clock_gettime(CLOCK_MONOTONIC, &tend);
                            
                                double te_ns = ((double)tend.tv_sec * BILLION + tend.tv_nsec);
                                double ts_ns = ((double)tstart.tv_sec * BILLION + tstart.tv_nsec);
                            
                                cout << (( te_ns - ts_ns ) / NUM ) << endl;
                            }
                            
                            int main(){
                                cout << "static var:         "; time_test(static_x_read);
                                cout << "volatile var:       "; time_test(volatile_static_x_read);
                                cout << "static mb var:      "; time_test(static_mb_x_read);
                                cout << " mfence var:        "; time_test(static_syn_syn_x_read);
                                cout << " syn syn var:       "; time_test(static_syn_syn_x_read);
                                cout << " lfence var:        "; time_test(static_lfence_x_read);
                                cout << " lfence inc var:    "; time_test(static_lfence_inc_x_read);
                                cout << "std::atomic var:    "; time_test(atomic_x_read);
                                cout << "std::atomic inc var:"; time_test(atomic_inc_x_read);
                                return 0;
                            }
                            Ответить
                          • ?
                            Ответить
                            • Ты утопил Борманда! Сволочь!
                              Ответить
                              • Не знаю, что означает "утопил", но вот то что, на поверку, не так много "крутых перцев" здесь обитает - это ФАКт.
                                Ответить
    • Ржач. Сюда пришёл поехавший вореций?
      Ответить
      • А я уж было подумал, что только мне это напоминает тот неповторимый дух вореционного авантюризма.

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

        В идеале, тут надо было вернуться к корням да воссоздать нечто в стиле традиционных кобенаций. Естественный фон здесь крайне благоприятен:
        10 ЖИРНЫХ нужно делать флаги 8 16 32 кобенаций даже 64 голов у не ломатб про портвбельность наверно 32 хороший вориант контексте
        Ответить
        • >А с повышением питульности выходит какая-то необычайная насыщенность мелкой твёрдой, отчего коэффициент Тараса падает, и всё насмарку.
          Вы необычайно наблюдательны, коллега.
          В связи с недопустимо низким качеством генерируемой мелко-мягкой психозы я вынужден был остановить производство, до выяснения причин брака и отлаживания технического процесса.
          Высокий питульный коэффициент, только одна из причин, но далеко не главная, почему наш продукт проигрывает конкуренциям вроде superhackkiller1997.
          Ответить
    • какую бы технологию не выбрали, оставьте оригинальные комментарии
      Ответить
      • Поставь себе уже что нибудь на аву, а то я не узнаю вас в гриме
        Ответить
        • пока не смотрел, завтра будет время отпишусь, думаю все отлично
          Ответить
    • ух ты бля, возьмите меня к себе ёба!
      Ответить
    • Золотой фонд
      Ответить

    Добавить комментарий