Как можно улучшить эту простую реализацию coroutine?

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

Я открыт для обратной связи как по почти чему-либо: совершенно разные подходы, как реструктурировать этот подход, ошибки, идиоматические указатели стиля C и т. д.

Очевидным улучшением производительности будет использование ucontext, но, похоже, он устарел и несколько разбит на Mac OS X (хотя он отлично работает с дистрибутивами Linux, которые я пробовал). Я не уверен, что следующий шаг - сделать это «лучше».

 typedef void (*coroutine_cb)();

static void scheduler(coroutine_cb);
static int spawn(coroutine_cb);
static void yield(int);

jmp_buf scheduler_jmp;
void *scheduler_rbp;
int coro_pid;

coroutine_cb scheduler_next_coro;

#define MAX_COROS 100

struct {
  jmp_buf jmp;
  void *stack;
  long stack_sz;
} coroutines[MAX_COROS];

static void scheduler(coroutine_cb coro)
{
  printf("starting the scheduler...\n");
  static int max_pid = 1;

  // we move down 0x1000 to give scheduler space to call functions and allocate stack variables without having them get
  // overwritten by the memcpy below. Before we did this, we had to manually copy memory instead of using memcpy
  // because we would overwrite memcpy's stack
  scheduler_rbp = __builtin_frame_address(0) - 0x1000;
  scheduler_next_coro = coro;
  int value = setjmp(scheduler_jmp);
  // value == 0 means just starting
  // value == -1 means spawning new coroutine
  // value positive means hop back to a specific pid
  if (value == 0 || value == -1)
  {
    coro_pid = max_pid++;
    printf("about to run coro %d...\n", coro_pid);
    char *buf = alloca(0x2000); // was 0x1000 when we didn't allocate extra space for scheduler stack
    asm volatile("" :: "m" (buf));
    scheduler_next_coro();
    assert(0);
  }
  else
  {
    printf("jumped back to scheduler (pid --> %d) (coro_pid --> %d)...\n", value, coro_pid);
    // restore coroutine marked by value (pid)
    coro_pid = value;

    int stack_sz; 
    stack_sz = coroutines[coro_pid].stack_sz;
    memcpy(scheduler_rbp - stack_sz, coroutines[coro_pid].stack, stack_sz);
    longjmp(coroutines[coro_pid].jmp, 1);
    assert(0);
  }
}

static void yield(int pid)
{
  // take current rbp
  void *rbp = __builtin_frame_address(0);
  void *fudgy_rsp = (char *)rbp - 0x100;
  assert(scheduler_rbp > rbp);

  long stack_sz = (char *)scheduler_rbp - (char *)fudgy_rsp;
  void *stack = malloc(stack_sz);

  /*
   * Marek: check how overflowing stack actually works, because
   * we're actually copying data beyond our stack frame.
   */
  memcpy(stack, fudgy_rsp, stack_sz);
  coroutines[coro_pid].stack = stack;
  coroutines[coro_pid].stack_sz = stack_sz;

  if (!setjmp(coroutines[coro_pid].jmp))
  {
    longjmp(scheduler_jmp, pid);
    assert(0);
  }
  else
  {
    // our stack is already good to go at this point
    return;
  }
}

/*
 *
 */
static int spawn(coroutine_cb coro)
{
  scheduler_next_coro = coro;
  yield(-1);
  return 0; // need to get pid
}

int main()
{
  scheduler(f);
  assert(0);
  return 0;
}
11 голосов | спросил dan 25 PM000000100000005331 2013, 22:13:53

2 ответа


7

Все, что у вас получилось:

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

Что можно улучшить:

Preprocessor:

  • Группируйте все свои #define в верхней части кода сразу после вашего #import s. В противном случае вам придется прыгать вокруг вашего кода, чтобы найти их.

Инициализация:

  • typedef ваш struct s.

    struct {
      jmp_buf jmp;
      void *stack;
      long stack_sz;
    } coroutines[MAX_COROS];
    

    typedef означает, что вам больше не нужно писать struct повсюду. Это не только экономит нажатия клавиш, но также может сделать код более чистым, поскольку он обеспечивает более абстракцию smidgen.

  • Вы можете сразу инициализировать некоторые переменные.

    int stack_sz; 
    stack_sz = coroutines[coro_pid].stack_sz;
    
    int stack_sz = coroutines[coro_pid].stack_sz;
    

Синтаксис:

  • Вы используете print(), где он не нужен.

    printf("starting the scheduler...\n");
    

    Вы можете использовать puts() .

    puts("Starting the scheduler...");
    

Память:

  • Вы выделили память на stack, но никогда не освобождаете ее.

    void *stack = malloc(stack_sz);
    

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

    free(stack);
    

Комментарии:

  • Вы можете использовать формат комментария /* ... */ вместо // ... при комментировании нескольких строк.

    // value == 0 means just starting
    // value == -1 means spawning new coroutine
    // value positive means hop back to a specific pid
    

    Вы также можете немного конденсировать свои комментарии.

    /* 
     * If 'value' is 0, it is just starting; if -1 it is spawning.
     * If 'value' is positive, we "hop" to a specific PID.
     */
    

Ресурсы:

ответил syb0rg 19 FebruaryEurope/MoscowbWed, 19 Feb 2014 05:27:04 +0400000000amWed, 19 Feb 2014 05:27:04 +040014 2014, 05:27:04
3

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

ответил 200_success 20 FebruaryEurope/MoscowbThu, 20 Feb 2014 02:21:06 +0400000000amThu, 20 Feb 2014 02:21:06 +040014 2014, 02:21:06

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132