Как обойти порядок файлов F # и зависимость типов

.net performance f#

221 просмотра

1 ответ

4529 Репутация автора

У меня есть следующая логическая структура моего проекта в F # (в порядке файлов):

Collection Interfaces
Base Implementations
----> Methods that work on interfaces
        | -----> IMathProvider that works on Specialized Implementations is injected during runtime 
Specialized Implementations that inherit base implementations.
MathProvider that knows about specialized implementations and implements IMathProvider

Обратите внимание, что MathProviderзависит от, Specialized Implementationsно используется выше внутри Base Implementations. Порядок файлов F # не позволяет мне использовать MathProviderнепосредственно внутри Base Implementations, и я должен сделать очень грязный хак в этой строке . (внутри статического конструктора базовой реализации это вызывает метод, определенный в самом низу, который знает обо всех реализациях и возвращает интерфейс).

Однако, поскольку MathProviderон возвращается как интерфейс, его методы никогда не являются кандидатами для встраивания в JIT, и недавно я узнал, что в .NET для высокопроизводительного числового кода очень важно. Кроме того, этот трюк с отражением настолько уродлив, что мне становится плохо ...

Мне нужна закрытая реализация класса MathProvider, которую можно будет оптимизировать для особых случаев. Основным случаем является структура данных, где ключи и значения являются массивами (SortedMap <,>, аналогично SCG.SortedList <,>). Так что мне не нужно знать о нем SortedMap<,>, но мне нужен доступ к двум массивам и размеру. Я хочу определить внутренний интерфейс на самом верхнем уровне:

internal interface IArrayBasedMap<TKey, TValue> {
    int length { get; }
    TKey[] Keys { get; }
    TValue[] Values { get; }
}

и заставить SortedMap<,>его реализовать. Затем я мог бы проверить, реализует ли коллекция этот интерфейс, и применить оптимизации к членам этого интерфейса. Оптимизация - это в основном вызовы P / Invoke для собственной математической библиотеки, поэтому я считаю, что накладные расходы на проверку типов и вызовы P / Invoke будут амортизироваться по сравнению с последовательным применением скалярной функции к каждому значению.

Считаете ли вы, что это правильный подход для преодоления упорядоченности файлов F # и зависимости типа сверху вниз в этом случае (против некрасивых размышлений при запуске)?

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

Автор: V.B. Источник Размещён: 13.07.2016 07:56

Ответы (1)


0 плюса

12087 Репутация автора

Каковы точки входа в вашу библиотеку?

Если их число ограничено, вы можете скрыть их под фасадом, который обеспечит вызов вашей логики инициализации. Давайте предположим, что пользователю необходимо создать SortedMap, как в вашем примере с github doc. Вместо того, чтобы пользователь вызывал конструктор, сделайте конструктор внутренним, и вместо этого получите что-то вроде этого:

type Spreads private () =
    static do Spreads.Initializer.init()

    static member SortedMap() = SortedMap()

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

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

Автор: scrwtp Размещён: 28.07.2016 03:52
32x32