實戰 hhvm extension php conf 2014
DESCRIPTION
HHVM 是 Facebook 開發的一個高效能 PHP 虛擬機用來取代行之有年的 Zend Engine。 但是,在獲得更高效能的同時也意味著原本 PECL 上的大量 Extension 必須一一改寫。 藉由這個議程可以了解到如何透過 HNI 界面,混用 C++ 以及 Hack Lang。 在效能以及開發速度上取得平衡,撰寫出屬於自己的 HHVM Extension。TRANSCRIPT
實戰 HHVM Extension
Ricky Su@phpconf2014
About me
Ricky 是我 哥
● Symfony 愛好者
● PHP也有Day 固定攝影師。
● 目前在 擔任高級水電工。
● http://www.facebook.com/ricky.su.35
● http://ricky.ez2.us/ (目前Server掛點中)
玩體驗
HHVM是什麼?
● 由 Facebook 於 2010年2月2日發布
● 將 PHP 轉換成 C++,再透過 g++ 編
譯執行檔。
● 於2013年2月19日被官方棄用。
HipHop (HPHPc)
● 透過 JIT 的方式加速 PHP 執行。
● 運行速度已經超越 HPHPc。
● 除了 PHP 外,還支援了 Hack Lang。
● 只支援 x64 平台,未來會支援 ARM。
● 目前 Facebook 的 PHP 程式皆運行在
HHVM 上。
HHVM
HHVM != PHP (Zend Engine)
HHVM 的執行效能?
fibonacci(40) 殘酷考驗
fibonacci(40)
<?php function fibonacci($n) { return $n<2?$n:fibonacci($n-1)+fibonacci($n-2); }
HHVM benchmark
Less is better
HHVM Extension benchmark
Less is better
但是開發
HHVM Extension
有個東西一定要了解
Hack Lang
Hack = 強型態的 PHP
Hack Lang
<?hhfunction fibonacci(int $n):int{ return $n<2?$n:fibonacci($n-1)+fibonacci($n-2); }
Hack Lang
<?hhfunction print_nl(?string $n = null): void{ echo "$n\n";}
HHVM Extension requirement
● x64 OS● gcc >= 4.7.3● g++ >= 4.7.3● cmake >= 2.8.3
HHVM Extension
寫起來很複雜嗎
只要三個檔案
就可以寫出
fibonacci
config.cmakeHHVM_EXTENSION( fibonacci fibonacci.cpp)HHVM_SYSTEMLIB( fibonacci ext_fibonacci.php)
ext_fibonacci.php<?hh<<__Native>> function fibonacci(int $n): int;
fibonacci.cpp#include "hphp/runtime/base/base-includes.h"namespace HPHP {static int64_t HHVM_FUNCTION(fibonacci, int64_t n) { return n<2?n:HHVM_FN(fibonacci)(n-2) + HHVM_FN(fibonacci)(n-1);}static class FibonacciExtension : public Extension { public: FibonacciExtension() : Extension("fibonacci") {} virtual void moduleInit() { HHVM_FE(fibonacci); loadSystemlib(); }} s_fibonacci_extension;HHVM_GET_MODULE(fibonacci)} // namespace HPHP
build extension
$ hphpize$ cmake .$ make && make install
config.cmakeHHVM_EXTENSION( fibonacci fibonacci.cpp foo.cpp bar.cpp other.cpp)
extensionname
config.cmakeHHVM_EXTENSION( fibonacci fibonacci.cpp foo.cpp bar.cpp other.cpp)
cpp files
config.cmakeHHVM_SYSTEMLIB( fibonacci ext_fibonacci.php)
PHP檔案只能一個而且命名必須是 ext_ 開頭
HNI (HHVM Native Interface)<?hh<<__Native>> function fibonacci(int $n): int;
function echo_ fibonacci(int $n): void{ echo fibonacci($n);}
register extenstionstatic class FibonacciExtension : public Extension { public: FibonacciExtension() : Extension("fibonacci") {} virtual void moduleInit() {
HHVM_FE(fibonacci);
loadSystemlib(); }} s_fibonacci_extension;HHVM_GET_MODULE(fibonacci)
register extenstionstatic class FibonacciExtension : public Extension { public: FibonacciExtension() : Extension("fibonacci") {} virtual void moduleInit() {
HHVM_FE(fibonacci);
loadSystemlib(); }} s_fibonacci_extension;HHVM_GET_MODULE(fibonacci)
定義函數
register extenstionstatic class FibonacciExtension : public Extension { public: FibonacciExtension() : Extension("fibonacci") {} virtual void moduleInit() { HHVM_FE(fibonacci);
HHVM_FE(foo);
loadSystemlib(); }} s_fibonacci_extension;HHVM_GET_MODULE(fibonacci)
如果有還有其他函數接著定義
register extenstionstatic class FibonacciExtension : public Extension { public: FibonacciExtension() : Extension("fibonacci") {} virtual void moduleInit() {
HHVM_FE(fibonacci);
loadSystemlib(); }} s_fibonacci_extension;HHVM_GET_MODULE(fibonacci)
讀取HNI
implement function
static int64_t HHVM_FUNCTION(fibonacci, int64_t n) { return n<2?n:HHVM_FN(fibonacci)(n-2) + HHVM_FN(fibonacci)(n-1);}
implement function
static int64_t HHVM_FUNCTION(fibonacci, int64_t n) { return n<2?n:HHVM_FN(fibonacci)(n-2) + HHVM_FN(fibonacci)(n-1);}
函數回傳值型態
implement function
static int64_t HHVM_FUNCTION(fibonacci, int64_t n) { return n<2?n:HHVM_FN(fibonacci)(n-2) + HHVM_FN(fibonacci)(n-1);}
函數名稱
implement function
static int64_t HHVM_FUNCTION(fibonacci, int64_t n) { return n<2?n:HHVM_FN(fibonacci)(n-2) + HHVM_FN(fibonacci)(n-1);}
參數型態定義
implement function
static int64_t HHVM_FUNCTION(foo, int64_t n, const String &n2) { // ...}
如果有多個參數接著定義
型態對應PHP Type C++ Parameter
TypeC++ Return
Typevoid N/A void
bool bool bool
int int64_t int64_t
float double double
string const String & String
array const Array & Array
resource const Resource & Resource
object const Object & Object
mixed const Variant & Variant
以上官方文件
都可以查到
但是
也只能查到這些了
如果要知道更多
RTFSC
接下來要踩地雷了
Native Class
HNI<?hhclass foo { protected ?mixed $bar; <<__Native>> public function __construct(mixed $bar): void; <<__Native>> public function getBarNative(): ?mixed; public function getBar():?mixed { return $this->bar; }}
register extenstionstatic class FooExtension : public Extension { public: FooExtension() : Extension("foo") {} virtual void moduleInit() { HHVM_ME(foo, __construct); HHVM_ME(foo, getBarNative); loadSystemlib(); }} s_foo_extension;HHVM_GET_MODULE(foo)
register extenstionstatic class FooExtension : public Extension { public: FooExtension() : Extension("foo") {} virtual void moduleInit() { HHVM_ME(foo, __construct); HHVM_ME(foo, getBarNative); loadSystemlib(); }} s_foo_extension;HHVM_GET_MODULE(foo)
註冊 foo__construct
implement class method
static void HHVM_METHOD( __construct, const Variant &bar) { this_->o_set("bar", bar, "foo");}
implement class method
static void HHVM_METHOD( __construct, const Variant &bar) { this_->o_set("bar", bar, "foo");}
對應 PHP 中的 $this
implement class method
static void HHVM_METHOD( __construct, const Variant &bar) { this_->o_set("bar", bar, "foo");}
存入成員變數
implement class method
static void HHVM_METHOD( __construct, const Variant &bar) { this_->o_set("bar", bar, "foo");}
$this->bar
implement class method
static void HHVM_METHOD( __construct, const Variant &bar) { this_->o_set("bar", bar, "foo");}
$this->bar = $bar
implement class method
static void HHVM_METHOD( __construct, const Variant &bar) { this_->o_set("bar", bar, "foo");}
指定目前的 context 為 foo
implement class method
static Variant HHVM_METHOD(getBarNative) { return this_->o_get("bar", false, "foo");}
implement class method
static Variant HHVM_METHOD(getBarNative) { return this_->o_get("bar", false, "foo");}
$this->bar
implement class method
static Variant HHVM_METHOD(getBarNative) { return this_->o_get("bar", false, "foo");}
如果讀取 $this->bar
發生錯誤時是否要丟出 error
implement class method
static Variant HHVM_METHOD(getBarNative) { return this_->o_get("bar", false, "foo");}
指定目前的 context 為 foo
Type Casting
Type Castingvar.toBoolean();
var.toDouble();
var.toString();
var.toInt64();
var.toArray();
Object Access
Object AccessC++ PHPobject.o_get(...) $object->...
object.o_set(...) $object->xxx = xxx
object.instanceof(class) object instanceof class
String Access
String AccessC++ PHPstring = “hello ” + “world” $string = “hello ” . ”world”
string += “foo” $string .= “foo”
string1 == string2 $string1 == $string2
string[1] $string[1]
string.substr(1, 2) substr($string, 1, 2)
string.size() strlen($string)
Array Access
Array AccessVariant var = array[1];
Variant var = array[String(“key”)];
Array AccessC++ PHParray.set(1, var) $array[1] = $var
array.set(String(“key”), var) $array[“key”] = $var
array.append(var) $array[] = $var
array = make_packed_array(1, 2, “val”) $array = [1, 2, “val”]
array = Array::Create() $array = []
Resource
Resource data declarenamespace HPHP {
class InternalResourceData : public SweepableResourceData { public: virtual const String& o_getClassNameHook() const { return classnameof(); } DECLARE_RESOURCE_ALLOCATION(InternalResourceData) CLASSNAME_IS("InternalResourceData") InternalResourceData(FILE *file); virtual ~InternalResourceData(); FILE *getHandler();
private: FILE *file; };
}
Resource data declarenamespace HPHP {
IMPLEMENT_OBJECT_ALLOCATION(InternalResourceData) InternalResourceData::InternalResourceData(FILE *file) { this->file = file; }
InternalResourceData::~InternalResourceData() { fclose(file); }
FILE *InternalResourceData::getHandler(){ return file; }
}
Resource usagestatic Resource HHVM_FUNCTION(open_file, const String &filename) { FILE *file = fopen(filename.c_str(), "r"); Resource resource(NEWOBJ(InternalResourceData(file))); return resource;}
static String HHVM_FUNCTION(read_file, const Resource &resource) { FILE * file; InternalResourceData *resource_data = resource.getTyped<InternalResourceData>(); file = resource_data->getHandler(); // ...}
Reference Counting
Variables
Value$aref count = 1
$a = “some value”;
Variables
Value$aref count = 2
$b = $a;
$b
Variables
Value$aref count = 1
$b = null;
$b
Variables
Value$aref count = 0
$a = null;
$b
Variables
Value$aref count = 0
$a = null;
$b
delete value
HHVM Variables
Variable ValueObject ObjectDataString StringDataArray ArrayData
Resource ResourceData
Increase referenceObject var = some_object;
Object *var = new Object(some_object);
ObjectDara *value = some_object->get();
value->incRefCount();
Decrease referenceObject *var = new Object(some_object);
delete var;
ObjectDara *value = some_object->get();
value->decRefAndRelease();
HHVM密(ㄉ一ˋ)技(ㄌㄟˊ)
HHVM Extension 第三方 Library
set callback
HHVM Extension 第三方 Library
call callback
HHVM Extension 第三方 Library
call callback
接著就GG了...Crash
都是 they 的錯gcc -O2
因為gcc -O2 預設開啟
omit-frame-pointer
導致 HHVM rbp 指標出錯
解決方法
重新編譯第三方套件
gcc 加上
-fno-omit-frame-pointer
但請放棄這個念頭
請改用神秘的
JIT::VMRegAnchor _;
VMRegAnchor
static void HHVM_FE(someFunction) { JIT::VMRegAnchor _; //then call 3rd party library;}
在呼叫第三方library之前加上這段就OK了。
取得Global Variable
取得Global VariableArray global(get_global_variables()->asArrayData());
Array _SERVER = global[StaticString("_SERVER")].toArray();
Array _GET = global[StaticString("_GET")].toArray();
Array _POST = global[StaticString("_POST")].toArray();
取得 Constant
取得 Constant#if HHVM_API_VERSION >= 20140829L //HHVM 3.3.0 #include "hphp/runtime/ext/std/ext_std_misc.h"#else // HHVM 3.2.0 #include "hphp/runtime/ext/ext_misc.h"#endif
Variant some_conatant = HHVM_FN(constant)(StaticString("SOME_CONSTANT"));
呼叫 PHP 的某個函數
呼叫 PHP 的某個函數
vm_call_user_func(StaticString("printf"), make_packed_array("%s %s", "hello", "world"));
printf(“%s %s”, “hello”, “world”);
或是直接
call native function
hphp/runtime/ext/*.h這裡幾乎實作了所有php的函數
Call native function#include "hphp/runtime/ext/std/ext_std_variable.h"
HHVM_FN(print_r)(var); //print_r($var);
還有更多地雷由您來踩
有問題嗎?
Thanks