برنامه نویسی

ما Elixir را تسریع می کنیم: ادغام با یک کد بومی (NIF ، درگاه ها و غیره)

مهم: قبل از معرفی زبانهای جدید به پروژه ، ابتدا اطمینان حاصل کنید که کد موجود در اکسیر تا حد امکان بهینه شده است. در بیشتر موارد ، بهینه سازی صالح کد فعلی به طور قابل توجهی عملکرد برنامه را بهبود می بخشد و گاهی اوقات نتایج بهتری نسبت به اضافه کردن زبان برنامه نویسی دیگر می دهد.

اکسیر وت الانگ – زبانهای عالی برای توسعه سیستم های مقیاس پذیر و مقصر. اما گاهی اوقات شما نیاز به فشار حداکثر عملکرد دارید یا از کتابخانه ای که فقط به زبان دیگری در دسترس است استفاده کنید. اگر در چنین شرایطی قرار دارید یا فقط می خواهید بدانید که چگونه دو زبان برنامه نویسی مورد علاقه خود را با هم ترکیب کنید ، این مقاله برای شما مناسب است!


فهرست مطالب

🔍 هنگامی که به یک کد بومی نیاز به ادغام دارید

قبل از اینکه به جزئیات فنی بپردازید ، درک آن مهم است کی ارزش این را دارد که ادغام با یک کد بومی را در نظر بگیرید:

1. وظایف محاسباتی و محاسباتی

پرتو برای رقابت ایجاد شد ، نه برای محاسبات. اگر مکان باریک شما عملیات محدود به CPU است:

  • 🧮 محاسبات ریاضی – جبر خطی یا عمل با ماتریس
  • 🔐 رمز رمزنگاری – رمزگذاری/رمزگشایی مقادیر زیادی از داده ها
  • 🎨 پردازش رسانه – تصاویر ، فیلم ، صوتی
  • 🧠 آموزش ماشین – استنباط مدل ها ، عملیات بردار

2. تعامل سخت افزار

در صورت نیاز به تجهیزات سطح پایین:

  • 📟 سیستم های تعبیه شده – تمشک پی ، میکروکنترلرها (برای این کار اعصاب در اکسیر وجود دارد ، اما گاهی اوقات ممکن است کافی نباشد)
  • 🎮 رانندگان خاص – دستگاه های غیر استاندارد
  • 📊 محاسبات GPU – CUDA ، OpenCl

3. استفاده مجدد از کد موجود

  • 🏛 کتابخانه های زمان بندی شده на c/c ++
  • 📚 مزایای اکوسیستم زبانهای دیگر (به عنوان مثال ، پایتون برای ML)
  • 🔧 اجتناب از “اختراع دوچرخه”

4. مشخصات و مکان های باریک

علائم معمولی که وقت آن است که در مورد کد بومی فکر کنیم:

# До оптимизации - 1000мс
defmodule SlowModule do
  def process_data(data) do
    # Потенциальное узкое место для нативной оптимизации
    Enum.reduce(data, 0, fn x, acc -> complex_calculation(x) + acc end)
  end

  defp complex_calculation(x) do
    # Представьте CPU-интенсивные вычисления
    # которые плохо масштабируются в BEAM
  end
end

# Вы исчерпали возможности оптимизации на Elixir и всё ещё медленно? 
# Возможно, пора подключать нативный код!
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

پسوندهای بومی اکسیر را به یک ابزار جهانی تبدیل می کنند ، جایی که پرتو همه چیز را به جز محاسبات مهم تصمیم می گیرد. این درها را به ML ، سخت افزار ، پردازش ویدیو و هر آنچه که سرعت مهم است باز می کند.


⚖ مقایسه روشهای ادغام

انتخاب روش ادغام صحیح برای موفقیت پروژه بسیار مهم است. در اینجا یک جدول مقایسه ای از روشهای ادغام با جوانب مثبت و منفی آنها آورده شده است:

روش سرعت پرتوی امنیتی پیچیدگی اجرای پشتیبانی زبانی هزینه های ارتباطی ناچیز سناریوهای استفاده کامل
نیل ⚡⚡⚡⚡⚡ ❌ خطرناک 🔧🔧🔧 متوسط ج ، ج ++ نه (تماس مستقیم) ❌ برنامه ریز را مسدود می کند خدمات میکروسرویس ، کارهای طولانی مدت CPU
کله ⚡⚡⚡⚡ ⚠ از نظر مشروط ایمن است 🔧🔧🔧 متوسط ج ، ج ++ نه (تماس مستقیم) ✅ برنامه ریز اصلی را مسدود نمی کند محاسبات طولانی (> 1ms)
بندر ⚡⚡ – روزها کاملاً بی خطر است 🔧 ساده هیچ بالا (IPC) ✅ انزوا پردازش پایتون ، برو ، اسکریپت های bash
محرک بندری ⚡⚡⚡⚡ ✅ ایمن 🔧🔧🔧🔧 پیچیده ج ، ج ++ کم stream جریان انتخاب شده پردازش ویدیو/صوتی
GRPC ⚡⚡ – روزها کاملاً بی خطر است 🔧🔧 متوسط هرکسی با پشتیبانی GRPC میانگین (شبکه) ✅ یک سرویس جداگانه معماری میکروسرویس
زنگ دار ⚡⚡⚡⚡ ✅ ایمن 🔧🔧 متوسط زنگ زدن نه (تماس مستقیم) ✅ پشتیبانی از API ناهمزمان جایگزین c nif
زیگل ⚡⚡⚡⚡⚡ ✅ ایمن 🔧🔧 متوسط زیور نه (تماس مستقیم) ✅ ایمنی NIF های استاندارد تر جایگزین C در NIF

مقایسه بصری توسط معیارهای کلیدی

Скорость:            Безопасность:         Простота:
NIF         █████    Port        █████     Port        █████
Dirty NIF   ████     Dirty NIF   ███       gRPC        ████
Rustler     ████     Rustler     ████      Rustler     ████
Zigler      █████    Zigler      ████      Zigler      ████
Port Driver ████     Port Driver ████      NIF         ███
Port        ██       gRPC        █████     Port Driver █
gRPC        ██       NIF         █         Dirty NIF   ███
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


🛠 کمی در مورد مکانیسم های ادغام

به

🧠 nifs – حداکثر عملکرد

توابع اجرا شده بومی (NIF) – سریعترین راه برای ادغام ، بلکه خطرناک ترین. آنها مستقیماً در جریان برنامه ریز پرتو انجام می شوند و به دلیل کمبود هزینه های سربار ، سرعت صاعقه را فراهم می کنند.

NIF چگونه کار می کند؟

  1. تالیف: کد بومی (C/Rust/ZIG) در کتابخانه پویا جمع آوری شده است (.soبا .dll)
  2. بار: پرتو در ابتدای ماژول کتابخانه را بارگیری می کند :erlang.load_nif/2
  3. اعدام مستقیم: توابع در همان جریان کد Evoking Elixir انجام می شوند

✅ مزایا

  • سرعت رعد و برق: این تماس 0.1-1 μs (100-1000 برابر سریعتر درگاه) طول می کشد
  • دسترسی به API پرتو: کار مستقیم با شرایط ارلانگ
  • عدم سریال سازی: بدون داده های کد نویسی/رمزگشایی سربار
  • گسترده: این کتابخانه با برنامه OTP همراه است

❌ مضرات

  • خطر سقوط همه VM: یک اشتباه در NIF کل پرتو را می کشد
  • مسدود کردن برنامه ریز: Polypo Long Nif Freeze
  • پیچیدگی اشکال زدایی: تشخیص نشت حافظه سخت است
  • وابستگی به سکو: تدوین برای هر معماری لازم است

NIF های کثیف – جایگزین ایمن

C erlang/OTP 20+ دفن شده است نفس کثیف – نوع خاصی از NIF ، که در استخر استخر برجسته انجام می شود ، که به شما امکان می دهد محاسبات طولانی را بدون مسدود کردن برنامه ریز پرتو انجام دهید.

// Определение Dirty NIF
static ErlNifFunc nif_funcs[] = {
    {"long_computation", 1, long_computation_nif, ERL_NIF_DIRTY_CPU}
};
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

💡 اتحاد جماهیر شوروی: استفاده کنید ERL_NIF_DIRTY_CPU برای عملیات محدود به CPU و ERL_NIF_DIRTY_IO برای عملیات ورودی/خروجی

🔌 پورت ها – انزوای کامل

درگاه -A روش تعامل با برنامه های خارجی از طریق جریان ورودی ورودی استاندارد (stdin/stdout). این امن ترین روش ادغام است ، زیرا برنامه خارجی در یک فرآیند سیستم عامل جداگانه راه اندازی می شود.

پورت ها چگونه کار می کنند؟

  1. راه اندازی: Elixir یک برنامه خارجی را به عنوان یک فرآیند سیستم عامل جداگانه راه اندازی می کند
  2. تبادل داده ها: ارتباط از طریق جریان استاندارد (stdin/stdout)
  3. عایق: سقوط برنامه خارجی بر پرتو تأثیر نمی گذارد

✅ مزایا

  • ایمنی کامل: جداسازی ثبات پرتو را تضمین می کند
  • چابکی زبان: با هر زبان برنامه نویسی کار می کند
  • سادگی اشکال زدایی: برنامه خارجی را می توان جداگانه آزمایش کرد
  • عدم وابستگی: برای پرتو نیازی به کتابخانه های خاص ندارد

❌ مضرات

  • هزینه های بالای سربار: 100-500 میکروگرم برای تماس
  • سریال سازی: تحول داده مورد نیاز است (معمولاً در JSON)
  • مسدود کردن تماس ها: به طور پیش فرض روند Defiant را مسدود می کند

🚗 درایورهای بندر – آکادمی طلایی

رانندگان – درگاه های جایگزین تولیدی ، اما پیچیده تر. اینها درایورهای زبان C هستند که مستقیماً در فضای آدرس پرتو ساخته شده و در جریان های جداگانه کار می کنند.

رانندگان بندر چگونه کار می کنند؟

  1. بار: پرتو C-Bibliotek را در فضای آدرس خود بار می کند
  2. انتشار جریان: راننده در یک جریان جداگانه کار می کند
  3. ناچیز: تبادل داده از طریق پیام ها

✅ مزایا

  • سرعت: خیلی سریعتر از بنادر معمولی
  • امنیت: خطر کمتری نسبت به NIF
  • ناچیز: پشتیبانی از عملیات غیر بسته شدن
  • راحتی: نیازی به راه اندازی یک فرآیند جداگانه ندارد

❌ مضرات

  • پیچیدگی: نیاز به دانش با api erlang
  • محدودیت: فقط با C/C ++ کار می کند
  • مستندات کمتر: نمونه ها و کتابچه های راهنمای زیادی نیست

🐊 زیگلر – بسته بندی بالای NIF برای زیگ

زیگل – کتابخانه ای که به شما امکان می دهد پسوندهای بومی را به زبان بنویسید زیوربشر کامپایلر ادغام می کند زیور به طور مستقیم در چرخه تدوین اکسیر و به شما امکان می دهد کد را مستقیماً بنویسید زیور در ماژول ها اکسیر

✅ مزایا

  • حداقل هزینه های سربار برای تماس با کد بومی
  • ایمنی حافظه بدون جمع کننده زباله
  • دسترسی مستقیم به عملیات سطح پایین
  • تدوین جامد در مقایسه با سایر پسوندهای بومی
  • پشتیبانی از کد بارگیری مجدد داغ

❌ مضرات

  • زیور – زبان نسبتاً جوان با یک جامعه کوچکتر
  • آستانه ورودی بالاتر برای توسعه دهندگان اکسیر
  • اکوسیستم محدود کتابخانه در مقایسه با زنگ زدن

Rustler – Operk بالاتر از nifs برای زنگ زدگی

Rustler یک کتابخانه برای ایجاد پسوندهای بومی Erlang/Elixir در زنگ زدگی است. اتصال ایمن بین Rust و Erlang/Elixir فراهم می کند و به شما امکان می دهد NIF ها را روی Rust بنویسید.

✅ مزایا

  • ایمنی حافظه در سطح تدوین
  • کارایی بالا برای کارهای محاسباتی پیچیده
  • اکوسیستم غنی از بسته های زنگ (جعبه)
  • محافظت در برابر نقص در یک کد بومی (محافظت از پرتو از افتادن)
  • اجرای موازی بدون مسدود کردن برنامه ریز پرتو

❌ مضرات

  • فرایند مونتاژ پیچیده و وابستگی
  • نیاز به دانش دو الگوی مختلف برنامه نویسی دارد
  • مشکلات احتمالی با سازگاری نسخه
  • مکان های باریک بالقوه در مرز رسانه های اجرا

🌐 GRPC و پروتکل های دیگر

برای سناریوهای پیچیده تر تعامل با خدمات خارجی ، به ویژه در معماری میکروسرویس ، GRPC و پروتکل های مشابه یک روش ادغام ساختاری و مقیاس پذیر را ارائه می دهند.

✅ مزایای GRPC برای اکسیر

  • طرح قراردادها: انواع دقیق از طریق بافر پروتکل
  • جریان دوتایی: پشتیبانی جریان در هر دو جهت
  • شکل: پشتیبانی از بسیاری از زبانها
  • عمل: مؤثرتر از REST/JSON

با GRPC این خیلی حجم بود که آن را در چارچوب این مقاله در نظر بگیریم ، به همین دلیل در آن گنجانده نشده است. اطلاعات بیشتر در مورد GRPC در Elixir را اینجا بخوانید – Elixir -grpc


📚 هدف از مقاله ، مجدداً روشهای موجود ادغام با یک کد بومی است. اجرای عمیق و موارد لبه در اینجا برای حفظ تعادل بین تئوری و استفاده کاهش می یابد.

معیارهای انتخاب یک روش ادغام

قبل از حرکت به مثالها ، عوامل اصلی انتخاب را تعیین می کنیم:

  1. عمل: تماس چقدر سریع کار می کند
  2. امنیت: خطر پرتو پایداری
  3. پیچیدگی اجرای: آیا اجرای یک راه حل آسان است
  4. پشتیبانی زبانی: چقدر زبان با پرتو کار می کند

1. ⚙ C – حداکثر عملکرد با درایورهای NIF و پورت

روشهای بهینه:

  • نیل – برای عملیات فوری (<1ms)
  • رانندگان – برای کارهای طولانی یا ناهمزمان

چرا است؟

C سازگاری مستقیم با پرتو دارد که باعث می شود آن را برای:

  • کارهای مهم از نظر عملکرد مهم است
  • عملیات سطح پایین (کار با آهن ، GPU)
  • ادغام با C-Bybliotexes موجود

مثال 1: NIF سریع برای هش (بهینه برای C)

// hash_nif.c
#include 
#include 

static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    ErlNifBinary input;
    if (!enif_inspect_binary(env, argv[0], &input)) {
        return enif_make_badarg(env);
    }

    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(input.data, input.size, hash);

    ErlNifBinary output;
    enif_alloc_binary(SHA256_DIGEST_LENGTH, &output);
    memcpy(output.data, hash, SHA256_DIGEST_LENGTH);

    return enif_make_binary(env, &output);
}

static ErlNifFunc nif_funcs[] = {
    {"sha256", 1, sha256_nif}
};

ERL_NIF_INIT(Elixir.CryptoNif, nif_funcs, NULL, NULL, NULL, NULL)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

قسمت اکسیر:

defmodule CryptoNif do
  @on_load :load_nif
  def load_nif do
    :erlang.load_nif(Path.expand("./hash_nif"), 0)
  end

  def sha256(_data), do: raise "NIF not loaded!"
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

منبع:

شما باید این مثال را شروع کنید OpensSL کتابخانه

# Для Ubuntu/Debian
sudo apt-get install libssl-dev
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

# Для CentOS/RHEL
sudo yum install openssl-devel
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

# Для macOS (если используете Homebrew)
brew install openssl
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

gcc -shared -fPIC -o hash_nif.so hash_nif.c \
  -I /usr/lib/erlang/erts-15.2.7/include \ # Путь до Erlang зависит от вашей системы
  -I /usr/include/openssl \
  -L /usr/lib/openssl \
  -lssl -lcrypto
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نتیجه:

iex(1)> CryptoNif.sha256("test")
<<159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8>>
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مثال 2: درایور پورت برای معکوس رشته

// reverse_driver.c
#include "erl_driver.h"
#include 

typedef struct {
    ErlDrvPort port;
} DriverData;

static void reverse_and_send(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) {
    DriverData* d = (DriverData*)drv_data;

    for (ErlDrvSizeT i = 0; i < len / 2; i++) {
        char temp = buf[i];
        buf[i] = buf[len - 1 - i];
        buf[len - 1 - i] = temp;
    }

    driver_output(d->port, buf, len);
}

static ErlDrvData driver_start(ErlDrvPort port, char* command) {
    DriverData* d = (DriverData*)driver_alloc(sizeof(DriverData));
    d->port = port;
    return (ErlDrvData)d;
}

static void driver_stop(ErlDrvData drv_data) {
    DriverData* d = (DriverData*)drv_data;
    driver_free(d);
}

static ErlDrvEntry driver_entry = {
    .init = NULL,
    .start = driver_start,
    .stop = driver_stop,
    .output = reverse_and_send,
    .ready_input = NULL,
    .ready_output = NULL,
    .driver_name = "reverse_driver",
    .finish = NULL,
    .handle = NULL,
    .control = NULL,
    .timeout = NULL,
    .outputv = NULL,
    .ready_async = NULL,
    .flush = NULL,
    .call = NULL,
    .extended_marker = ERL_DRV_EXTENDED_MARKER,
    .major_version = ERL_DRV_EXTENDED_MAJOR_VERSION,
    .minor_version = ERL_DRV_EXTENDED_MINOR_VERSION,
    .driver_flags = 0,
    .handle2 = NULL,
    .process_exit = NULL,
    .stop_select = NULL
};


DRIVER_INIT(reverse_driver) {
    return &driver_entry;
}

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

قسمت اکسیر:

defmodule ReverseString do
  def start() do
    :ok = :erl_ddll.load_driver('./', 'reverse_driver')
    Port.open({:spawn, 'reverse_driver'}, [:binary])
  end

  def reverse(port, string) when is_binary(string) do
    true = Port.command(port, string)
    receive do
      {^port, {:data, result}} -> result
    after
      1000 -> {:error, :timeout}
    end
  end

  def stop(port) do
    Port.close(port)
  end
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

منبع:

gcc -std=gnu99 -shared -fPIC -o reverse_driver.so reverse_driver.c \
    -I/usr/lib/erlang/erts-15.2.7/include/ # Путь до Erlang зависит от вашей системы
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نتیجه:

iex(1)> port = ReverseString.start
#Port<0.6>
iex(2)> ReverseString.reverse(port, "hello")
"olleh"
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. 🦀 زنگ زدگی – ایمنی و عملکرد با Rustler

روش بهینه: Rustler (بسته بندی تخصصی بالاتر از NIF)

چرا پولالر؟

  • ایمنی کامل حافظه
  • ماکروهای مناسب برای کار با شرایط پرتو
  • پردازش خطای خودکار
  • پشتیبانی از کارهای ناهمزمان

مثال: پردازش داده های موازی

اضافه کردن به mix.exe Rustler (در حال حاضر نسخه فعلی – 0.36.1)

  defp deps do
    [
      {:rustler, "~> 0.36.1"}
    ]
  end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

پروژه Rust را به Elixir اضافه کنید

mix deps.get
mix rustler.new

This is the name of the Elixir module the NIF module will be registered to.
Module name > RustUtils
This is the name used for the generated Rust crate. The default is most likely fine.
Library name (rustutils) > # Тут просто нажимаем Enter (Ну или переименуйте на более удобное название)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

// lib.rs
use rayon::prelude::*;

#[rustler::nif]
fn parallel_double(input: Vec<i64>) -> Vec<i64> {
    input.par_iter().map(|&x| x * 2).collect()
}

rustler::init!("Elixir.RustUtils");
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

قسمت اکسیر:

defmodule RustUtils do
  use Rustler, otp_app: :rust_utils, crate: "rustutils"

  def parallel_double(_list), do: :erlang.nif_error(:not_loaded)
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نتیجه:

RustUtils.parallel_double([1, 2, 3])
# => [2, 4, 6]
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. 🐍 پایتون – سهولت ادغام از طریق درگاه ها

روش بهینه: بنادر

چرا بنادر؟

  • عایق کامل فرآیندها
  • سادگی اشکال زدایی
  • دسترسی به کل Excount Python
  • پشتیبانی از عملیات طولانی (مدل های ML و غیره)

مثال: ایجاد مدل و ادغام خود با TensorFlow

ما یک مدل ایجاد می کنیم

# create_model.py
import tensorflow as tf
import numpy as np

model = tf.keras.Sequential([
    tf.keras.layers.Dense(1, input_shape=(3,), use_bias=False)
])

model.set_weights([np.array([[1.0], [1.0], [1.0]])])

model.save("my_model.h5")
print("✅ Model saved to my_model.h5")
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

#tensorflow_port
import sys
import json
import numpy as np
import tensorflow as tf
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
tf.get_logger().setLevel('ERROR')

model = tf.keras.models.load_model('my_model.h5')

def predict(data):
    try:
        if isinstance(data, str):
            data = json.loads(data)

        input_array = np.array(data, dtype=np.float32).reshape(1, 3)
        prediction = model.predict(input_array)
        return json.dumps({"status": "success", "result": prediction.tolist()})
    except Exception as e:
        return json.dumps({"status": "error", "message": str(e)})

if __name__ == "__main__":
    for line in sys.stdin:
        line = line.strip()
        if not line:
            continue

        try:
            response = predict(line)
            sys.stdout.write(response + "\n")
            sys.stdout.flush()
        except Exception as e:
            error = json.dumps({"status": "error", "message": str(e)})
            sys.stdout.write(error + "\n")
            sys.stdout.flush()

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

قسمت اکسیر:

defmodule TensorflowPort do
  @timeout 5_000

  def start do
    Port.open(
      {:spawn, "python3 tensorflow_port.py"},
      [:binary, :use_stdio, :exit_status, :stderr_to_stdout, {:line, 1024}]
    )
  end

  def predict(port, input_data) do
    input_data
    |> Jason.encode!()
    |> then(&Port.command(port, &1 <> "\n"))

    wait_for_response(port)
  end

  defp wait_for_response(port) do
    receive do
      {^port, {:data, {:eol, line}}} ->
        case Jason.decode(line) do
          {:ok, %{"status" => "success", "result" => result}} -> {:ok, result}
          {:ok, %{"status" => "error", "message" => msg}} -> {:error, msg}
          _ -> wait_for_response(port)
        end

      {^port, {:exit_status, status}} ->
        {:error, "Python process exited with status #{status}"}
    after
      @timeout -> {:error, :timeout}
    end
  end
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نتیجه:

برای کار در این برنامه ، باید به رد پایتون بروید و از آنجا شروع کنید iex -S mix

(venv)
iex(1)> port = TensorflowPort.start
#Port<0.10>
iex(2)> TensorflowPort.predict(port, [1,2,3])
{:ok, [[6.0]]}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

4. 🐹 کارآیی از طریق C-Wings یا GRPC

روشهای بهینه:

  • بال های C برای NIF-وقتی به حداکثر عملکرد نیاز دارید
  • GRPC – برای تعامل پیچیده و خدمات ریزگردها

C-Drawing برای کد GO (بهینه برای عملکرد)

// fib.go
package main

import "C"

func GoFib(n C.int) C.int {
    a, b := 0, 1
    for i := 0; i < int(n); i++ {
        a, b = b, a+b
    }
    return C.int(a)
}

func main() {}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بال های:

// c_src/go_nif.c
#include 
#include "libfib.h"

static ERL_NIF_TERM go_fib_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    int n;
    if (!enif_get_int(env, argv[0], &n)) {
        return enif_make_badarg(env);
    }
    return enif_make_int(env, GoFib(n));
}

static ErlNifFunc nif_funcs[] = {
    {"go_fib", 1, go_fib_nif}
};

ERL_NIF_INIT(Elixir.NifGo, nif_funcs, NULL, NULL, NULL, NULL)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

قسمت اکسیر:

defmodule NifGo do
  @on_load :load_nif

  def load_nif do
    nif_path = Path.expand("priv/go_nif", File.cwd!())

    # Предварительная загрузка Go-библиотеки
    :erlang.load_nif(Path.expand("priv/libfib", File.cwd!()), 0)

    # Загрузка основной NIF-библиотеки
    :erlang.load_nif(nif_path, 0)
  end

  def go_fib(_n), do: raise("NIF not loaded!")
end

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نتیجه:

go build -buildmode=c-shared -o priv/libfib.so fib.go

gcc -shared -fPIC \
    -I/usr/lib/erlang/erts-15.2.7/include/ \ # Путь до Erlang зависит от вашей системы
    -I./priv \
    -o priv/go_nif.so \
    c_src/go_nif.c \
    ./priv/libfib.so \
    -Wl,-rpath,'$ORIGIN'

LD_LIBRARY_PATH=./priv iex -S mix

iex(1)> NifGo.go_fib(3)
2
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اگر نمی خواهید با تالیف و ABI اشتباه بگیرید ، می توانید به جای CGO از GRPC یا پورت ها استفاده کنید. این ایمن تر است ، گرچه کندتر.

5. ⚡ زیگ – ادغام از طریق زیگلر

روش بهینه: زیگلر (بسته بندی تخصصی برای زیگ)

چرا زیگلر؟

  • سادگی مانند Rustler
  • بهره وری مانند C
  • ویژگی های مدرن زبان (comptime و غیره)
  • ایمن تر از یک C خالص

مثال: پردازش سریع داده های باینری

Zigler را به Mix.exe اضافه کنید:

def deps do
  [
    {:zigler, "~> 0.13.2", runtime: false}
  ]
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

lib/nif_zig.ex:

defmodule NifZig do
  use Zig, otp_app: :zigler

  ~Z"""
  pub fn string_count(string: []u8) i64 {
    return @intCast(string.len);
  }

  pub fn list_sum(array: []f64) f64 {
    var sum: f64 = 0.0;
    for(array) | item | {
      sum += item;
    }
    return sum;
  }
  """
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نتیجه:

iex(3)> NifZig.string_count("hello")
5
iex(4)> NifZig.list_sum([1.2,2.3,3.4,4.5])
11.4
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

📊 خلاصه جدول روشهای بهینه

زبان روش بهینه طرف دیگر چه زمانی استفاده کنید
جف NIF ، درایورهای بندر درگاه حداکثر عملکرد
زنگ زدن Rustler (NIF) جدید ایمنی + عملکرد
پیتون درگاه GRPC ادغام با ML/کتابخانه های علمی
رفتن C-Obrats (NIF) ، GRPC درگاه استفاده از GO برای اکوسیستم
زیور زیگلر (NIF) جدید جایگزین مدرن ج.

😴 نتیجه گیری

ادغام اکسیر با یک کد بومی افق های جدیدی را برای توسعه دهندگان باز می کند و به شما امکان می دهد مزایای پرتو (مقیاس پذیری ، تحمل شکست) را با بهره وری و کتابخانه های سایر زبانها ترکیب کنید. در مقاله ، ما روشهای کلیدی ادغام را بررسی کردیم: NIF ، Dirty NIF ، درگاه ها ، درایورهای بندر ، Rustler ، Zigler و GRPC و همچنین سناریوهای بهینه استفاده آنها.

به یاد داشته باشید که انتخاب روش ادغام باید بر اساس الزامات خاص پروژه شما برای عملکرد ، ایمنی و سهولت توسعه باشد.


از نویسنده

از توجه شما به این مقاله متشکرم! امیدوارم که شما علاقه مند به یادگیری در مورد راه های ادغام سایر زبانها در اکسیر باشید. از بین همه گزینه ها ، به نظر من دشوارترین درایورهای بندری است ، زیرا ایجاد یک راننده به خودی خود به یک تلاش واقعی تبدیل می شود ، تنها پس از چند ساعت انتخاب روشهای مناسب در C. با موفقیت تکمیل شد.

اگر نادرستی را در مطالب پیدا کردید یا اضافات جالبی دارید – لطفاً در مورد آن در نظرات بنویسید. بازخورد سازنده همیشه ارزشمند است

برای کسانی که علاقه مند به عمیق تر در دنیای فناوری ، معماری و توسعه هستند ، من از شما دعوت می کنم به کانال تلگرام خود نگاه کنید ، جایی که من بررسی کتابهای فنی ، مطالب مفید در اکسیر و نه تنها 🤤 را به اشتراک می گذارم.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا