現代程式語言需要泛型

語言: CN / TW / HK

作者丨 Ayende Rahien

譯者丨明知山

策劃丨閆園園

幾周前,我寫了一篇關於程式語言 Hare 及其缺少泛型資料結構的文章。如今,我不想再討論這個話題了,我想討論一些更“泛型”的東西。在我看來,任何以高效能為目標的現代程式語言都應該支援某種形式的泛型,不支援泛型是一個重大錯誤,也是導致複雜性增加和效能損失的一大原因。與一次性實現相比,泛型資料結構得到了更多的優化,我已經在前一篇文章中談到了這一點。

另外,如果不支援泛型,就會在優化方面面臨巨大的障礙。你根本就無法構建某些輔助程式。舉個例子,我們來談談我最關心的一個話題——排序。處理排序資料是資料庫的一個重要任務,其他的東西都是以它為基礎。我們來看看如何使用幾種程式語言 (使用它們的定義) 對資料 (在記憶體中) 進行排序。

C 語言:

void qsort (void *array, size_t count, size_t size, comparison_fn_t compare);
int comparison_fn_t (const void *, const void *);

C++:

template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);

Java:

public static void sort(int [] a);
public static void sort(long[] a);
public static void sort(Object[] a);

C#:

public static void Sort<T> (T[] array);

Hare:

type cmpfunc = fn(a: const *void , b: const *void ) int ;
fn sort([]void , size, *cmpfunc) void ;

Rust:

impl<T> [T] {
pub fn sort(&mut self)
where
T: Ord,
}

Zig:

pub fn sort(
comptime T: type,
items: []T,
context: anytype,
comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
) void

我只看方法宣告,而不是實現。事實上,我現在並不關心它們是如何實現的。假設我想對一個整數陣列排序,使用這些語言會有怎樣的結果?

它們可以分為以下幾類:

C 語言和 Hare 會要求你這麼寫:

int cmp_asc_int(const void *a, const void *b) {
return *(int*)a > *(int*)b;
}


qsort(array, len, sizeof(int), cmp_asc_int);

也就是說,我們傳了一個函式指標給排序例程,在每次比較時會呼叫它。

C++、C#、Rust、Zig 會對例程進行優化。在呼叫時,看起來是這樣的:

std::sort(array.begin(), array.end());

其原理是編譯器能夠針對呼叫發出(emit)程式碼。與每次呼叫都必須執行一次函式不同,比較操作通常是內聯的,並且完全消除了呼叫成本。

Java 是這些語言當中唯一採用了不同方法的。它沒有在編譯時使用泛型,而是根據執行時型別將程式碼分派給優化的例程。當然,這意味著程式設計師必須多次編寫相同的排序程式碼。

需要注意的是,這並不是什麼新奇的東西。在 Go 語言增加泛型支援時就有過相關的討論,從基準測試可以看出,泛型版本有了 20% 的效能提升。這是因為避免了呼叫開銷,併為編譯器提供了更多的優化機會。

我們可以看到,一個相對簡單的決定 (讓語言支援泛型) 是如何對效能產生巨大影響的。

相反的觀點認為,我們總是可以根據需要專門化程式碼,對吧?但事實並非如此。如果有泛型,你就可以免費獲得這種行為,但如果沒有,就不是這麼回事了。

我以開發資料庫為生,我們通常會在彙編級別分析我們排序程式碼的效能。我相信,幾乎每個資料庫開發人員都會這麼做。排序效能對資料庫的一切行為來說都是非常關鍵的。我偶然看到一篇關於 Postgres 效能優化的文章,其中有一個有趣的話題討論的就是這個問題。他們將排序的實現從使用函式指標改為直接呼叫。你可以在這裡看到提交的程式碼。下面是程式碼截圖:

Postgres 已經 25 歲了,而這也是 C 語言相對於 C++ 的一個眾所周知的弱點。Postgres 進行了很多排序呼叫,而這是一個很容易實現效能優化的地方。

至於優化效果,從這篇博文可以看出,優化讓整體效能提高了 4% 到 6%。對於那些特定的例程來說,效果是相當驚人的。

對於一個擁有 25 年曆史的程式碼庫來說,一個相對簡單的變更就可以帶來大約 6% 的效能提升,這樣的場景是非常少見的。

但是,我為什麼要用這種方式說出來呢?

因為當我讀到這篇博文時,它提及的優化手段與之前關於泛型的討論產生了強烈的共振。這是針對這個問題的一個很好的研究案例,因為如果語言 (對 Postgres 來說是 C 語言) 沒有以任何有意義的方式提供泛型支援,優化就很難進行,而且代價巨大。

以效能為目標的現代程式語言在進行語言設計時應該重視這一點。如果不這麼做,使用者將不得不做一些類似於 Postgres 正在做的事情。正如我們剛才看到的,這類事情是不完美的。

沒有泛型意味著使用者不得不將效能束之高閣。

實際上,幾乎所有關心高效能的現代程式語言都有泛型。我能想到的一個例外是 Java,這是因為它在新增泛型時選擇了向後相容。

我將本文作為上一篇關於泛型資料結構的文章的補充結論,我認為最終的結果是顯而易見的。如果你想要高效能的系統,就應該選擇一種能讓你簡潔地表達邏輯的程式語言,而泛型是實現這種簡潔性的必要工具。

原文連結:

https://ayende.com/blog/197282-B/modern-programming-languages-require-generics ?

關聯閱讀:

https://ayende.com/blog/197185-B/criticizing -hare-language-approach-for-generic-data-structures ?

今日文章推薦:

GitLab 新版本釋出,搶人大戰再升級

博通欲收購 VMware,多次易手後找到新下家

活動推薦:

隨著分散式資料庫的熱度持續走高,分散式資料庫的發展現狀、技術發展以及技術選型等問題儼然處在了話題中央。

對於開發者及企業來說,仍然存在許多問題有待被解答。

如何才能全域性洞察分散式資料庫,乃至整個資料庫行業的走向與發展脈絡?

如何能夠快速 Get 分散式資料庫的架構選型思想?

掃描下方二維碼或點選【閱讀原文】,下載沙利文聯合頭豹研究院釋出的《2021 年中國分散式資料庫市場報告》,即可為你解決上述問題!