Как создать последовательность из 200-500 тысяч номеров всего за несколько микросекунд - PullRequest
1 голос
/ 08 февраля 2020

Мне нужно создать последовательность чисел в кратчайшие сроки, в идеале в диапазоне микросекунд. Общая длина этой последовательности обычно находится в диапазоне 20–100 тысяч чисел, но иногда мне нужно создать последовательность из 200–300 тысяч чисел. На данный момент я реализовал свой код на R (просто потому, что я достаточно хорошо знаю язык). Предполагая, что моя последовательность должна начинаться с 1 и заканчиваться до 25 с шагом 0,0001, в RI будет

s <- seq(1, 25, by = 0.0001)

Хотя это невероятно быстро, для завершения

> library(microbenchmark)
> microbenchmark(seq(1, 25, by = 0.0001))
Unit: milliseconds
                   expr    min      lq     mean median     uq     max neval
 seq(1, 25, by = 1e-04) 1.6098 2.14505 3.741858 2.2324 2.3554 83.8912   100
* 1006 требуется несколько миллисекунд * Вот мои вопросы:

1 - возможно ли ускорить генерацию последовательности в R? в идеале <0,5 миллисекунды? </p>

2- в какой-то момент мне нужно будет реализовать код на другом (более быстром) языке, сможет ли какой-либо язык генерировать такие последовательности менее чем за 100-200 микросекунд?

Ответы [ 2 ]

5 голосов
/ 08 февраля 2020

Используя Rcpp, вы можете улучшить скорость на ~ 60%. Тем не менее, до сих пор не несколько микросекунд.

library(Rcpp)
library(microbenchmark)

cppFunction(
  "
  NumericVector cpp_seq(double &from, double &to, double &by){
    int length = abs((to - from) / by);

    NumericVector out(length + 1);
    int pos = 0;

    for (double i = from; i < to; i += by){
      out[pos] = i;
      pos +=1;
    }

    out[length] = to;
    return out;
  }
  "
)

x <- cpp_seq(1, 25, 0.0001)

microbenchmark(
  seq = seq(1, 25, 0.0001),
  cpp_seq = cpp_seq(1, 25, 0.0001), 
  times = 1000
)

# Unit: microseconds
#     expr      min        lq     mean   median       uq      max neval
#     seq  2425.694 2630.6595 4262.708 2847.697 3256.618 99978.10  1000
# cpp_seq   670.949  794.6625 1239.710  903.714 1005.736 12892.09  1000

Оба вектора одинаковы, если учесть ошибки с плавающей запятой:

x_R <- seq(1, 25, 0.0001)
x_cpp <- cpp_seq(1, 25, 0.0001)

identical(round(x_R, 9), round(x_cpp, 9))
# [1] TRUE
2 голосов
/ 08 февраля 2020

seq.int() на 30-40% быстрее ..

microbenchmark::microbenchmark(
  seq = seq(1, 25, by = 0.0001),
  seq.int = seq.int(1, 250000, by = 1) / 10000
)

# Unit: milliseconds
#    expr    min      lq     mean median      uq     max neval
#     seq 2.0749 2.16525 3.375812 2.2222 2.31935 95.9553   100
# seq.int 1.3184 1.35860 1.458503 1.3855 1.42940  6.9268   100

Также остерегайтесь ошибок с плавающей точкой !!

sprintf("%.100f", seq(0,0.01,0.0001) )
  [1] "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
  [2] "0.0001000000000000000047921736023859295983129413798451423645019531250000000000000000000000000000000000"
  [3] "0.0002000000000000000095843472047718591966258827596902847290039062500000000000000000000000000000000000"
  [4] "0.0003000000000000000279290479632265942200319841504096984863281250000000000000000000000000000000000000"
  [5] "0.0004000000000000000191686944095437183932517655193805694580078125000000000000000000000000000000000000"
  [6] "0.0005000000000000000104083408558608425664715468883514404296875000000000000000000000000000000000000000"
  [7] "0.0006000000000000000558580959264531884400639683008193969726562500000000000000000000000000000000000000"
...