Я написал «серверную» программу, которая пишет в общую память, и клиентскую программу, которая читает из памяти. Сервер имеет разные «каналы», в которые он может записывать, это просто разные связанные списки, к которым он также добавляет элементы. Клиент интересуется некоторыми связанными списками и хочет прочитать каждый узел, добавленный в эти списки, по мере его поступления с минимально возможной задержкой.
У меня есть 2 подхода к клиенту:
Для каждого связанного списка клиент сохраняет указатель «закладки», чтобы сохранить свое место в связанном списке. Он циклически перебирает связанные списки, перебирает все их снова и снова (он зацикливается навсегда), перемещая каждую закладку на один узел вперед каждый раз, когда это возможно. Может ли он, определяется значением «следующего» члена узла. Если он не равен нулю, то переход к следующему узлу безопасен (сервер переключает его с нуля на ненулевой атомарно). Этот подход работает нормально, но если есть много списков для перебора, и только немногие из них получают обновления, задержка становится плохой.
Сервер присваивает каждому списку уникальный идентификатор. Каждый раз, когда сервер добавляет элемент в список, он также добавляет идентификационный номер списка в основной «список обновлений». Клиент сохраняет только одну закладку, закладку в списке обновлений. Он бесконечно проверяет, не является ли следующий указатель закладки ненулевым (while(node->next_ == NULL) {}
), если он движется вперед, читает заданный идентификатор, а затем обрабатывает новый узел в связанном списке, который имеет этот идентификатор. Это, теоретически, должно лучше обрабатывать большое количество списков, потому что клиент не должен каждый раз повторять их все.
Когда я измерял задержку обоих подходов (используя gettimeofday), к моему удивлению, # 2 был ужасен. Первый подход для небольшого числа связанных списков часто будет с задержкой до 20 мкс. Второй подход будет иметь небольшие разряды с низкой задержкой, но часто будет между 4000-7000US!
Путем вставки gettimeofday здесь и там, я определил, что вся добавленная задержка в подходе # 2 тратится в цикле, многократно проверяя, является ли следующий указатель ненулевым. Это озадачивает меня; как будто изменение одного процесса занимает больше времени, чтобы «опубликовать» второй процесс при втором подходе. Я предполагаю, что происходит какое-то взаимодействие с кешем, которого я не понимаю. Что происходит?
Обновление: Первоначально подход # 2 использовал переменную условия, поэтому, если node->next_ == NULL
, он будет ожидать условия, и сервер будет уведомлять об условии каждый раз, когда он выпустил обновление. Задержка была такой же, и при попытке выяснить, почему я сократил код до подхода, описанного выше. Я работаю на многоядерном компьютере, поэтому спинлокирование одного процесса не должно влиять на другое.
Обновление 2: узел-> следующий_ изменчив.