LINUX.ORG.RU

ASM CS:RIP store'n'load

 , ,


0

2

Привет, лор.

Предыстория: нужно сделать некоторый функционал, который позволит повторять код без использования циклов (на самом деле задача сложнее, но углубляться пока нету смысла). В чем бы сложность, спросит читатель, goto, jmp в помощь. А вот суть такова, что неизвестно сколько программист-пользователь захочет расставить таких «меток». Более того, его удобство требует, чтобы это были некоторые макросы. Пример:

void foo()
{
   MACRO_INIT()
   int i = 0;
   MACRO()
   i++
   MACRO()
   i--
   MACRO_FIN()
}

Собственно, между макросами что-то несколько раз повторяется. И макросов этих может быть сколько угодно. Собственно, метки как-то не вариант. Есть два возможных варианта решения, которые были придуманы: писать препроцессор, который будет генерировать и ставить метки в код; прыгать через связку cs:rip, сохраняя и загружая состояния в макросах. Оба варианта геморные и грязные, но второй удобнее для пользователя.

Вопрос: есть ли какое-то лучшее решение, чем такие грязные хаки?

Бонусом отмечу, что задача, в теории, еще сложнее: эти макросы могут быть расположены вне вызова foo() и считаться должны по цепочке, т.е. должен еще очищаться стек от того, что туда положилось от вызова функции. Да и переходы в другие операторные блоки тоже. Но хочется решать задачу постепенно.

Ответ на: комментарий от erfea

Если б студентам давали такие странные вещи, как моя — они бы взвыли.

JAkutenshi ★★
() автор топика

setjmp/longjmp

А вообще если ты делаешь не обработку самодельных исключений и не корутины, то ты что-то делаешь очень сильно не так. Остановись и подумай.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

setjmp/longjmp

О, вот это мне сильно больше нравится. Попробую.

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

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

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

KivApple ★★★★★
()
Ответ на: комментарий от KivApple

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

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

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

KivApple ★★★★★
()
Ответ на: комментарий от JAkutenshi

Не очень понял к чему тут они.

Курс по операционным системам MIT содержит в себе задания по написанию корутин. Там как раз можно попрактиковаться в написании сохранения/переключения контекста. По сути это и есть переход к нужным частям кода без циклов.

xpahos ★★★★★
()
Ответ на: комментарий от xpahos

Простите, я просто не могу увязать сохранение контекста (стека, регистров) и переход к нужным частям кода. Вот, честно. Одно про сохранение в, например, память (завести свой стек и натравить на него ss:rsp), а другое про ret и jmp far. Возможно очень поздно сегодня.

JAkutenshi ★★
() автор топика
Ответ на: комментарий от xpahos

А, понял. Да, конечно, перед тем, как начинать повторять какой-то код сохраняется контекст. Это понятно. Вопрос больше как возвращаться в этот чекпоинт. cs и rip я успешно сохраняю, но подменить их просто нельзя. Это либо через стек, либо через jmp far

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Именно для этого и делаются итерации. Ставится их количество в целом и количество первых «холодных», которые отбрасываются. Если хочется померять весьма случайные результаты холодного замера - ставятся 1 и 0 соответственно. Я уже это просматривал на первой, кривой версии бенчмарка, когда и повыявлял кучу проблем (в частности еще надо учитывать оптимизации компилятора)

JAkutenshi ★★
() автор топика

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

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

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)

Очень напоминает дискретно-событийную модель.

dave ★★★★★
()
Ответ на: комментарий от SZT

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

Да.. нет? Там человек верно подметил про идею корутинов и сохранения состояния программы. В общем-то единственный косяк тут будет в возможных утечках памяти по очевидным причинам, но они будут в любом случае при таком подходе, как и в случае setjmp/longjmp.

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

решать твою задачу надо сильно другим способом

Ну, оттого и тред ,что вдруг кто-то делал что-то похожее и/или видит другую концепцию решения.

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

Там человек верно подметил про идею корутинов и сохранения состояния программы.

Не, ну, можно тупо клонировать процесс в замороженном состоянии

В мане на системный вызов clone сказано:

CLONE_STOPPED (since Linux 2.6.0-test2)
If CLONE_STOPPED is set, then the child is initially stopped (as though it was sent a SIGSTOP signal), and must be resumed by sending it a SIGCONT signal.

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

Сохранять полностью состояние программы не выйдет, потому как есть (могут быть) еще файлы, открытые программой, которые могут меняться другими программами, и могут быть какие-нибудь семафоры и shared memory , и как все это откатить? Виртуалкой только, ИМХО.

Еще есть: https://www.criu.org/Main_Page и https://github.com/google/snappy-start

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 2)
Ответ на: комментарий от JAkutenshi

Я делаю бенчмарк монитор.

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

anonymous
()

А тебе обязательно C и C++? Рассматриваются ли другие языки?

Существуют разные способы прерывать контекст выполнения, а потом возвращаться в него. Тебе это нужно?

Если C/C++, то вроде бы для них пока ничего лучше сопрограмм не придумали (??), но это только для этих двух языков. С языками ФП выбор богаче, а реализация проще.

dave ★★★★★
()
Ответ на: комментарий от SZT

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

Чего ходить вокруг да около: мобильные роботы, фреймворки ROS, orocos, MIRA. Их особенности: распределенная архитектура, подгрузка агентов-модулей при помощи .so модулей в рантайме, очень затруднительно вырвать логику агентов отдельно от всей их системы. Поэтому измерение должно быть максимально встроенным, в частности из-за их архитектурных решений. Я тогда думал, что дело-лафа, берем гугл-бенчмарк и лепим костыли, пока не приполз к orocos и mira.

Собственно, тогда я сделал заголовочник с макросами с часами и rdtsc, которые порасставлял осторожно и померял. Тогда постарался сделать это максимально точно, при помощи итеративного выполнения участков кодов, чтобы рандома на кэше было меньше. Собственно, вот это усложнение потока команд и приводит сейчас к проблеме. Мое решение было хорошо, когда это работает внутри какой-нибудь одной функции на линейной логике (сформировал пакет -> отдал). Добавление нелинейности, вложенности, уже не поддерживается моим решением. Примеры сценариев привел выше. Или, например, хочется померять время работы функции извне и внутри.

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

JAkutenshi ★★
() автор топика
Ответ на: комментарий от dave

Да, предметная область жестко завязана на C/C++, я и сам не думал, что когда-нибудь буду писать таки на крестах, но что поделать.

JAkutenshi ★★
() автор топика
Ответ на: комментарий от Iron_Bug

Причем, да, важное замечание таки было, ибо такие замеры «мелких» участков будут весьма синтетическими, но стабильными. Но этот подход не будет полезен, если ищутся какие-то узкие места системы, это не профилирование. Т.е. именно для профилирования лучше такое: использовать простые таймеры, одним детерминированным проходом померять значение (если нужна стат. обработка, то повторять от начала и до конца несколько раз), количество исполнений тестов. А для сравнительного теста производительности, создание максимально стерильных условий, итераций, разбиение на достаточно маленькие участки кода, чтобы уменьшить влияние сложно предсказуемых оптимизаций процессора. Так по-идее корректнее?

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

Может тебе нужно что-то типа systemtap/dtrace/etc.?

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

Deleted
()
Ответ на: комментарий от JAkutenshi

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

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от Deleted

systemtap/dtrace

Спасибо, гляну их. Не уверен, что то, что нужно, но как вариант профилирования — почему бы и нет.

Из сообщений не очень понятно

Да, я это заметил и дал комментарий чуть выше: ASM CS:RIP store'n'load (комментарий) Возникло это, скорее-всего, потому что изначально мною написанный инструмент (для сравнения) начал применяться в другой области (для профилирования). Хорошо, что я это как-то осознал.

JAkutenshi ★★
() автор топика
Ответ на: комментарий от Iron_Bug

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

JAkutenshi ★★
() автор топика

Весело тут. Меряют общую производительность на не реалтайм-платформе x86, где любая операция может зависнуть на неопределеное время. Надеюсь хоть процессоры не «моложе» 486.

anonymous
()
Ответ на: комментарий от anonymous

Звучит как «мы не можем гарантировать, значит тестировать вообще не надо, матстат тоже не помогает людям», ну.

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

Нудык, ты это про это и пишешь здесь, ну. Измеряют измеряемое, парадокс, но так сложилось.

anonymous
()
Ответ на: комментарий от anonymous

Измеряют, например, линейкой, которая тоже несовершенна, поэтому даже у линейки есть точность и погрешность измерений, а не «ну, я не могу с точностью до атома измерить, поэтому длина не измеряема и измерять не будем». Это уже оффтоп как-то.

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

Да, предметная область жестко завязана на C/C++

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

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

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
void foo()
{
   MACRO_INIT()
   int i = 0;
   MACRO()
   i++
   MACRO()
   i--
   MACRO_FIN()
}

И что я из этого должен понять? Я не понимаю что ты хочешь. Хочешь пускать макросы с int i = 0; контекстом?

void foo() {
  int x = 0;
  ([=]() mutable {
    ++x;
  })();
  
  
  ([=]() mutable {
    --x;
  })();
}

Хочешь много раз пускать каждую функцию?

void foo() {
  int x = 0;
  auto f0 = [=]() mutable {
    ++x;
  };
  
  
  auto f1 = [=]() mutable {
    --x;
  };
  
  f0();
  f0();
  
  //хочешь прочитаь?
  //можешь захачить:
  
  auto print = [=]() mutable {
    fprintf(stderr, "%d\n", x);
  };
  
  ((decltype(print) &)f0)();
  
  //можешь сделать попроще:
  
  auto ctx = [=]() mutable {
    
    auto run = [&] {
      ++x;
    };
    
    run();
    run();
    
    fprintf(stderr, "%d\n", x);
    
  };
  ctx();
  
}

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

Пока что - я даже не понимаю что тебе нужно.

anonymous
()
Ответ на: комментарий от JAkutenshi

Собственно, тогда я сделал заголовочник с макросами с часами и rdtsc, которые порасставлял осторожно и померял. Тогда постарался сделать это максимально точно, при помощи итеративного выполнения участков кодов, чтобы рандома на кэше было меньше. Собственно, вот это усложнение потока команд и приводит сейчас к проблеме. Мое решение было хорошо, когда это работает внутри какой-нибудь одной функции на линейной логике (сформировал пакет -> отдал). Добавление нелинейности, вложенности, уже не поддерживается моим решением. Примеры сценариев привел выше. Или, например, хочется померять время работы функции извне и внутри.

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


start = rdtsc;
++i;
time = rdtsc - start;


//но теперь у тебя там:
f() {
  start = rdtsc;
  ++i;
  time = rdtsc - start;
}
f() x 10050;

//и проблема у тебя в том, что ты не знаешь - как объединить эти 100500 вызовов в одно время? К тому же, эти вызовы могут происходить из разных источников и ты хочешь измерять вызовы именно из одного источника?


Сошлюсь на свой предыдущий вопрос - из твоих объяснений нихрена непонятно.

anonymous
()
Ответ на: комментарий от vvviperrr

это который? jos?

Да, он в себя включает jos.

xpahos ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.