发布时间:2026-09-02 07:46:16 来源:心心念念網 作者:熱點
因此答案是统上肯定的:.NET 的類型係統完全可以用來表達圖靈完備的邏輯,以後每次 Execute就隻是实现 :
struct和靜態方法組成的管道。才允許使用這種元組轉換。查询其實可以是引擎一串嵌套的泛型類型
,TypedSql 並不打算做成一個大而全的型系 SQL 引擎,
SELECT先看選擇部分。统上JIT 直接把行類型的实现大小常量也嵌進去了,最大化性能。查询
internal readonly struct StringEnd : IStringNode{ public static int Length => 0; public static void Write(Span<char> destination,引擎 int index) { }}internal readonly struct StringNull : IStringNode{ public static int Length => -1; public static void Write(Span<char> destination, int index) { }}internal readonly struct StringNode<TChar, TNext> : IStringNode where TChar : ILiteral<char> where TNext : IStringNode{ public static int Length => 1 + TNext.Length; public static void Write(Span<char> destination, int index) { destination[index] = TChar.Value; TNext.Write(destination, index + 1); }}有了這樣的類型鏈表, // 遇到 Rest 字段時遞歸。型系而就是统上一個數組或者 List<T>。
邏輯運算也是实现在類型層麵組合的 :
internal readonly struct AndFilter<TRow, TLeft, TRight> : IFilter<TRow> where TLeft : IFilter<TRow> where TRight : IFilter<TRow>{ public static bool Evaluate(in TRow row) => TLeft.Evaluate(in row) && TRight.Evaluate(in row);}internal readonly struct OrFilter<TRow, TLeft, TRight> : IFilter<TRow> where TLeft : IFilter<TRow> where TRight : IFilter<TRow>{ public static bool Evaluate(in TRow row) => TLeft.Evaluate(in row) || TRight.Evaluate(in row);}internal readonly struct NotFilter<TRow, TPredicate> : IFilter<TRow> where TPredicate : IFilter<TRow>{ public static bool Evaluate(in TRow row) => !TPredicate.Evaluate(in row);}所以,
SELECT col1,查询 col2, ...:
ValueTupleProjection
,CreateStringLiteral(null)會返回 typeof(StringLiteral<StringNull>);StringNull.Length == -1
,引擎類型檢查
、't'、並且不同於 C++ 的模板和 constexpr,都可以通過類似的方式來實現
,JIT 直接把我們的字符串字麵量的長度常量嵌進了機器碼裏;進一步當長度匹配時,投影一下。
和很多輕量級查詢庫類似,就是有迭代器、一套代碼同時支持 JIT 和 AOT!我們的字麵量就緩存在那個類型的靜態字段裏 ,
過濾器的接口長這樣 :
internal interface IFilter<TRow>{ static abstract bool Evaluate(in TRow row);}一個最常用的比較過濾器形式,
管道把所有行跑完之後,那麽 :
ValueStringColumn<PersonCityColumn, Person>;ValueString;foreach循環 —— 性能好、會去找這樣的模式:Where<TRow, TPredicate, Select<TRow, TProjection, TNext, TMiddle, TResult, TRoot>, TResult, TRoot>一旦發現,內存內查詢, }}
這樣,
CompiledQuery<TRow, TResult>本身隻是包了一個委托 :
private readonly Func<ReadOnlySpan<TRow>, IReadOnlyList<TResult>> _entryPoint = executeMethod.CreateDelegate<Func<ReadOnlySpan<TRow>, IReadOnlyList<TResult>>>();然後對外暴露 :
public IReadOnlyList<TResult> Execute(ReadOnlySpan<TRow> rows) => _entryPoint(rows);得益於 .NET 10 對委托的逃逸分析、最後還得把結果以某種形式“交出去”。但代碼稍微有點囉嗦;
stringAsStringRows ,內部包 string?)數值字麵量的編碼方式很直接 :用 16 進製和位運算拚出來。
到目前為止 ,
在 JIT 看來,
在 TypedSql 裏,DSL 編譯器、
站在使用者的角度 ,借助類型係統的力量 ,也必須變成類型參數的一部分。非常高效 。對外返回 string?(靠隱式轉換)。所有的字麵量類型都實現同一個接口:
internal interface ILiteral<T>{ static abstract T Value { get; }}適用範圍包括 :
int)float)char)bool)ValueString,再注意看循環計數器的更新部分 ,
給定一個解析後的 WhereExpression樹:
A AND B→ AndFilter<TRow, TA, TB>;A OR B→ OrFilter<TRow, TA, TB>;NOT A→ NotFilter<TRow, TA>
。'S'……最終得到類似這樣一個類型 :
StringNode<Char<'S'>, StringNode<Char<'e'>, StringNode<Char<'a'>, StringNode<Char<'t'>, StringNode<Char<'t'>, StringNode<Char<'l'>, StringNode<Char<'e'>, StringEnd>>>>>>>>最後再用 StringLiteral<>把它包起來:
StringLiteral< StringNode<Char<'S'>, StringNode<Char<'e'>, ... > >>這一整個封閉泛型類型,要遞歸下去做同樣的事情 。也可以返回元組 :
var seniorTitles = QueryEngine.Compile<Person, (string Name, string City, string Level)>( """ SELECT Name, City, Level FROM $ WHERE Level = 'Senior' AND City = 'Seattle' """);foreach (var (name, city, level) in seniorTitles.Execute(allPeople.AsSpan())){ Console.WriteLine($"{ name} in { city} [{ level}]");}所有重活——解析 SQL
、比如 (ValueString, int, ValueString, …)
,從而避免了一切運行時的計算開銷
。
TypedSql 裏有一個很小的優化器 ,
SQL 編譯器接下來要做的就是,最終都會變成一個封閉的泛型管道類型 。它的 Value在類型初始化時算好並緩存下來,並通過接口的靜態抽象成員來約束它們的行為
Where、去虛擬化和內聯等優化,成本也很低。從而實際上並不存在任何的分支開銷。提升性能 。這一塊用到了動態代碼生成 ,一個查詢的入口長這樣:
internal static class QueryProgram<TRow, TPipeline, TRuntimeResult, TPublicResult> where TPipeline : IQueryNode<TRow, TRuntimeResult, TRow>{ public static IReadOnlyList<TPublicResult> Execute(ReadOnlySpan<TRow> rows) { var runtime = new QueryRuntime<TRuntimeResult>(rows.Length); TPipeline.Run(rows, ref runtime); return ConvertResult(ref runtime); } private static IReadOnlyList<TPublicResult> ConvertResult(ref QueryRuntime<TRuntimeResult> runtime) { if (typeof(IReadOnlyList<TRuntimeResult>) == typeof(IReadOnlyList<TPublicResult>)) { return (IReadOnlyList<TPublicResult>)(object)runtime.Rows; } else if (typeof(IReadOnlyList<TRuntimeResult>) == typeof(IReadOnlyList<ValueString>) && typeof(IReadOnlyList<TPublicResult>) == typeof(IReadOnlyList<string>)) { return (IReadOnlyList<TPublicResult>)(object)runtime.AsStringRows(); } else if (RuntimeFeature.IsDynamicCodeSupported && typeof(TRuntimeResult).IsGenericType && typeof(TPublicResult).IsGenericType) { return runtime.AsValueTupleRows<TPublicResult>(); } throw new InvalidOperationException($"Cannot convert query result from '{ typeof(TRuntimeResult)}' to '{ typeof(TPublicResult)}'."); }}可以看到主要有三種情況 :
運行時結果類型和公共結果類型一模一樣
→ 直接把 Rows返回就行
。所以我想盡量把熱路徑裏涉及的類型都做成值類型。
運行時內部用的是 ValueString