Ускорить генерацию произвольного кода FsCheck

Я пишу несколько генераторов и Arbitrary, но это слишком медленно (см. также номера GC). Я думаю, что у меня есть ошибка в моем коде, но я не могу понять, где. Или мой подход (map2 (fold)) "странный"?

Генераторы:

type Generators () =

    static let notAllowed = Array.append [| '0'..'9' |] [| '\n'; '\r'; '['; ']'; '/'; |] 

    static let containsInvalidValues (s : string)  = s.IndexOfAny(notAllowed) <> -1

    static member positiveIntsGen() = Arb.generate<PositiveInt> |> Gen.map int

    static member separatorStringGen() =                         
        Arb.generate<NonEmptyString> 
        |> Gen.suchThat (fun s -> s.Get.Length < 5 && not (s.Get |> containsInvalidValues))

Произвольный:

let manyNumbersNewLineCustomDelimiterStrInput = 
    Gen.map2 (fun (ints : int[]) (nes : NonEmptyString) ->             
        Array.fold (fun acc num ->                 
            if num % 2 = 0 then acc + "," + num.ToString()
            else if num % 3 = 0 then acc + "\n" + num.ToString()
            else acc + "\n" + num.ToString()) ("//[" + nes.Get + "]\n") ints ) 
        (Generators.array12OfIntsGen()) 
        (Generators.separatorStringGen())
    |> Arb.fromGen

Конфигурация имеет MaxTest = 500, и ее выполнение занимает ~ 5 минут.

Вывод (используя #timer):

StrCalcTest.get_When pass an string that starts with "//[" and contains "]\n" use the multicharacter value between them as separator-Ok, passed 500 tests.

Real: 00:07:03.467, CPU: 00:07:03.296, GC gen0: 75844, gen1: 71968, gen2: 4

person nicolocodev    schedule 21.05.2016    source источник


Ответы (1)


Ничего не тестируя, я предполагаю, что проблемная часть заключается в следующем:

Arb.generate<NonEmptyString> 
|> Gen.suchThat (fun s -> s.Get.Length < 5 && not (s.Get |> containsInvalidValues))

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

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

Не могли бы вы, например, сгенерировать число n (для длины строки), за которым следуют n значений типа char (которые удовлетворяют вашим условиям), а затем добавить их для формирования строки-разделителя? (Я думаю, что вычисление gen { .. } FsCheck может быть хорошим способом написать это.)

person Tomas Petricek    schedule 21.05.2016
comment
Вы были правы, спасибо! В итоге я получил что-то вроде этого. Последний быстрый вопрос: знаете ли вы, возможно ли создать Generators из статического типа в файле fsx? я не могу :( - person nicolocodev; 22.05.2016
comment
Я не знаю - извините! - person Tomas Petricek; 22.05.2016