提交 ec5a9620 编写于 作者: 微希夷 提交者: yitter

增加PHP扩展

上级 ebf2fd07
*.lo
*.la
.libs
.deps
acinclude.m4
aclocal.m4
autom4te.cache
build
config.guess
config.h
config.h.in
config.log
config.nice
config.status
config.sub
configure
configure.ac
include
install-sh
libtool
ltmain.sh
# Makefile
Makefile.fragments
Makefile.global
Makefile.objects
missing
mkinstalldirs
modules
run-tests.php
tests/*/*.diff
tests/*/*.out
tests/*/*.php
tests/*/*.exp
tests/*/*.log
tests/*/*.sh
.vscode/
vendor/
language: php
compiler:
- gcc
- clang
os:
- linux
php:
- 7.0
- 7.1
- 7.2
- 7.3
- 7.4
- 8.0
# - nightly
notifications:
email: 63851587@qq.com
env:
- REPORT_EXIT_STATUS=1 NO_INTERACTION=1
before_install:
- chmod +x ./travis/compile.sh
- chmod +x ./travis/run-tests.sh
#Compile
before_script:
- ./travis/compile.sh
# Run PHPs run-tests.php
script:
- ./travis/run-tests.sh
\ No newline at end of file
snowdrift
63851587@qq.com
\ No newline at end of file
--------------------------------------------------------------------
The PHP License, version 3.01
Copyright (c) 1999 - 2011 The PHP Group. All rights reserved.
--------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
modification, is permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. The name "PHP" must not be used to endorse or promote products
derived from this software without prior written permission. For
written permission, please contact group@php.net.
4. Products derived from this software may not be called "PHP", nor
may "PHP" appear in their name, without prior written permission
from group@php.net. You may indicate that your software works in
conjunction with PHP by saying "Foo for PHP" instead of calling
it "PHP Foo" or "phpfoo"
5. The PHP Group may publish revised and/or new versions of the
license from time to time. Each version will be given a
distinguishing version number.
Once covered code has been published under a particular version
of the license, you may always continue to use it under the terms
of that version. You may also choose to use such covered code
under the terms of any subsequent version of the license
published by the PHP Group. No one other than the PHP Group has
the right to modify the terms applicable to covered code created
under this License.
6. Redistributions of any form whatsoever must retain the following
acknowledgment:
"This product includes PHP software, freely available from
<http://www.php.net/software/>".
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------
This software consists of voluntary contributions made by many
individuals on behalf of the PHP Group.
The PHP Group can be contacted via Email at group@php.net.
For more information on the PHP Group and the PHP project,
please see <http://www.php.net>.
PHP includes the Zend Engine, freely available at
<http://www.zend.com>.
# ❄ idenerator-php-extension
## 介绍
项目更多介绍参照:https://github.com/yitter/idgenerator
## PHP环境
* PHP7 or later
## 安装方式
```shell
git clone https://gitee.com/yitter/idgenerator.git
cd idgenerator/PHP
phpize
./configure --with-php-config=/path/php-config
make
make install
```
## 如何使用(PHP)
**配置文件配置参数**
```shell
//snowdrift.ini
snowdrift.Method=1 //1 漂移算法 2 传统算法
snowdrift.BaseTime=1582136402000
snowdrift.WorkerId=1 //默认workerid
snowdrift.WorkerIdNum=1 //支持的WorkerId数量,默认1,不超过(-1L << snowdrift.WorkerIdBitLength) ^ -1L
snowdrift.WorkerIdBitLength=6
snowdrift.SeqBitLength=6 //自增序号位数
snowdrift.MaxSeqNumber=0
snowdrift.MinSeqNumber=0
snowdrift.TopOverCostCount=2000 //最大漂移次数
```
**函数签名**
```php
\SnowDrift::NextId(int $wid=snowdrift.WorkerId):?int //获取单个id,$wid可选,默认值=snowdrift.WorkerId
\SnowDrift::NextNumId(int $num, int $wid=snowdrift.WorkerId):?array //获取$num个id,$wid可选,默认值=snowdrift.WorkerId
```
**调用示例**
```php
$id=\SnowDrift::NextId();
$id=\SnowDrift::NextId(3);
$ids=\SnowDrift::NextNumId(10000);
$ids=\SnowDrift::NextNumId(10000,3);
```
dnl $Id$
dnl config.m4 for extension snowdrift
dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(snowdrift, for snowdrift support,
dnl Make sure that the comment is aligned:
dnl [ --with-snowdrift Include snowdrift support])
dnl Otherwise use enable:
PHP_ARG_ENABLE(snowdrift, whether to enable snowdrift support,
dnl Make sure that the comment is aligned:
[ --enable-snowdrift Enable snowdrift support])
if test "$PHP_SNOWDRIFT" != "no"; then
dnl Write more examples of tests here...
dnl # get library FOO build options from pkg-config output
dnl AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
dnl AC_MSG_CHECKING(for libfoo)
dnl if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists foo; then
dnl if $PKG_CONFIG foo --atleast-version 1.2.3; then
dnl LIBFOO_CFLAGS=`$PKG_CONFIG foo --cflags`
dnl LIBFOO_LIBDIR=`$PKG_CONFIG foo --libs`
dnl LIBFOO_VERSON=`$PKG_CONFIG foo --modversion`
dnl AC_MSG_RESULT(from pkgconfig: version $LIBFOO_VERSON)
dnl else
dnl AC_MSG_ERROR(system libfoo is too old: version 1.2.3 required)
dnl fi
dnl else
dnl AC_MSG_ERROR(pkg-config not found)
dnl fi
dnl PHP_EVAL_LIBLINE($LIBFOO_LIBDIR, SNOWDRIFT_SHARED_LIBADD)
dnl PHP_EVAL_INCLINE($LIBFOO_CFLAGS)
dnl # --with-snowdrift -> check with-path
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this
dnl SEARCH_FOR="/include/snowdrift.h" # you most likely want to change this
dnl if test -r $PHP_SNOWDRIFT/$SEARCH_FOR; then # path given as parameter
dnl SNOWDRIFT_DIR=$PHP_SNOWDRIFT
dnl else # search default path list
dnl AC_MSG_CHECKING([for snowdrift files in default path])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl SNOWDRIFT_DIR=$i
dnl AC_MSG_RESULT(found in $i)
dnl fi
dnl done
dnl fi
dnl
dnl if test -z "$SNOWDRIFT_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([Please reinstall the snowdrift distribution])
dnl fi
dnl # --with-snowdrift -> add include path
dnl PHP_ADD_INCLUDE($SNOWDRIFT_DIR/include)
dnl # --with-snowdrift -> check for lib and symbol presence
dnl LIBNAME=snowdrift # you may want to change this
dnl LIBSYMBOL=snowdrift # you most likely want to change this
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SNOWDRIFT_DIR/$PHP_LIBDIR, SNOWDRIFT_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_SNOWDRIFTLIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([wrong snowdrift lib version or lib not found])
dnl ],[
dnl -L$SNOWDRIFT_DIR/$PHP_LIBDIR -lm
dnl ])
dnl
dnl PHP_SUBST(SNOWDRIFT_SHARED_LIBADD)
snowdrift_source_file="snowdrift.c\
src/snowflake/snowflake.c\
src/snowflake/shm.c\
src/snowflake/spinlock.c
"
PHP_NEW_EXTENSION(snowdrift, $snowdrift_source_file, $ext_shared)
fi
// $Id$
// vim:ft=javascript
// If your extension references something external, use ARG_WITH
// ARG_WITH("snowdrift", "for snowdrift support", "no");
// Otherwise, use ARG_ENABLE
ARG_ENABLE("snowdrift", "enable snowdrift support", "no");
if (PHP_SNOWDRIFT != "no") {
THIS_DIR=`dirname $0`
snowdrift_source_file="snowdrift.c\
$THIS_DIR/src/snowflake/snowflake.c
"
EXTENSION("snowdrift", $snowdrift_source_file, PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
}
/*
+----------------------------------------------------------------------+
| snowdrift |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: 63851587@qq.com |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifndef PHP_SNOWDRIFT_H
#define PHP_SNOWDRIFT_H
extern zend_module_entry snowdrift_module_entry;
#define phpext_snowdrift_ptr &snowdrift_module_entry
#define PHP_SNOWDRIFT_VERSION \
"1.0.0" /* Replace with version number for your extension */
#ifdef PHP_WIN32
#define PHP_SNOWDRIFT_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#define PHP_SNOWDRIFT_API __attribute__((visibility("default")))
#else
#define PHP_SNOWDRIFT_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
// PHP8
#if PHP_VERSION_ID >= 80000
#define TSRMLS_D void
#define TSRMLS_DC
#define TSRMLS_C
#define TSRMLS_CC
#define TSRMLS_FETCH()
#define ZEND_ACC_DTOR 0
#endif
ZEND_BEGIN_MODULE_GLOBALS(snowdrift)
uint8_t Method;
uint64_t BaseTime;
uint8_t WorkerId;
uint8_t WorkerIdNum;
uint8_t WorkerIdBitLength;
uint8_t SeqBitLength;
uint32_t MaxSeqNumber;
uint32_t MinSeqNumber;
uint16_t TopOverCostCount;
ZEND_END_MODULE_GLOBALS(snowdrift)
ZEND_DECLARE_MODULE_GLOBALS(snowdrift)
#define SD_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(snowdrift, v)
#if defined(ZTS) && defined(COMPILE_DL_SNOWDRIFT)
ZEND_TSRMLS_CACHE_EXTERN()
#endif
static int snowdrift_init();
#endif /* PHP_SNOWDRIFT_H */
/*
+----------------------------------------------------------------------+
| snowdrift |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: 63851587@qq.com |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "zend_exceptions.h"
#include "ext/standard/info.h"
#include "src/snowflake/shm.h"
#include "php_snowdrift.h"
#include "src/snowflake/snowflake.h"
/* True global resources - no need for thread safety here */
static struct shm shmctx;
static snowflake *sf;
zend_class_entry snowdrift_ce;
uint8_t num = 0;
/* {{{ PHP_INI */
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("snowdrift.Method", "1", PHP_INI_SYSTEM, OnUpdateLongGEZero, Method, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.BaseTime", "1582136402000", PHP_INI_SYSTEM, OnUpdateLongGEZero, BaseTime, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.WorkerId", "1", PHP_INI_SYSTEM, OnUpdateLongGEZero, WorkerId, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.WorkerIdNum", "1", PHP_INI_SYSTEM, OnUpdateLongGEZero, WorkerIdNum, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.WorkerIdBitLength", "6", PHP_INI_SYSTEM, OnUpdateLongGEZero, WorkerIdBitLength, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.SeqBitLength", "6", PHP_INI_SYSTEM, OnUpdateLongGEZero, SeqBitLength, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.MaxSeqNumber", "0", PHP_INI_SYSTEM, OnUpdateLongGEZero, MaxSeqNumber, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.MinSeqNumber", "0", PHP_INI_SYSTEM, OnUpdateLongGEZero, MinSeqNumber, zend_snowdrift_globals, snowdrift_globals)
STD_PHP_INI_ENTRY("snowdrift.TopOverCostCount", "2000", PHP_INI_SYSTEM, OnUpdateLongGEZero, TopOverCostCount, zend_snowdrift_globals, snowdrift_globals)
PHP_INI_END()
/* }}} */
static int snowdrift_init()
{
num = (-1L << SD_G(WorkerIdBitLength)) ^ -1L;
if (SD_G(WorkerIdNum) < num)
{
num = SD_G(WorkerIdNum);
}
shmctx.size = num * sizeof(snowflake);
if (shm_alloc(&shmctx) == -1)
{
zend_throw_exception_ex(NULL, 0, "shared memory malloc failed");
RETURN_FALSE;
}
if (SD_G(MaxSeqNumber) <= SD_G(MinSeqNumber))
{
zend_throw_exception_ex(NULL, 0, "MaxSeqNumber must GE then MinSeqNumber");
RETURN_FALSE;
}
bzero(shmctx.addr, num * sizeof(snowflake));
sf = (snowflake *)shmctx.addr;
for (int i = 0; i < num; i++)
{
snowflake *tmp = (sf + i);
tmp->Method = SD_G(Method);
tmp->BaseTime = SD_G(BaseTime);
tmp->WorkerId = i + 1;
tmp->WorkerIdBitLength = SD_G(WorkerIdBitLength);
tmp->SeqBitLength = SD_G(SeqBitLength);
tmp->MaxSeqNumber = SD_G(MaxSeqNumber);
tmp->MinSeqNumber = SD_G(MinSeqNumber);
tmp->TopOverCostCount = SD_G(TopOverCostCount);
Config(tmp);
}
return SUCCESS;
}
PHP_METHOD(snowdrift, NextId)
{
zend_long wid = SD_G(WorkerId);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &wid) == FAILURE)
{
RETURN_FALSE;
}
wid--;
if (wid < 0 || wid > num - 1)
{
zend_throw_exception_ex(NULL, 0, "wid error! wid between 0 and %d", num - 1);
RETURN_NULL();
}
snowflake *flake = (sf + wid);
RETURN_LONG(NextId(flake));
}
/** 这种方式性能比不上PHP直接循环调用NextId快
*/
PHP_METHOD(snowdrift, NextNumId)
{
zend_long num = 1;
zend_long wid = SD_G(WorkerId);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &num, &wid) == FAILURE)
{
RETURN_FALSE;
}
wid--;
if (wid < 0 || wid > num - 1)
{
zend_throw_exception_ex(NULL, 0, "wid error! wid between 0 and %d", num - 1);
RETURN_NULL();
}
snowflake *flake = (sf + wid);
array_init(return_value);
for (int i = 0; i < num; i++)
{
add_next_index_long(return_value, NextId(flake));
}
}
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(snowdrift)
{
UNREGISTER_INI_ENTRIES();
shm_free(&shmctx);
return SUCCESS;
}
/* }}} */
/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(snowdrift)
{
#if defined(COMPILE_DL_SNOWFLAKE) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
return SUCCESS;
}
/* }}} */
/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(snowdrift)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(snowdrift)
{
php_info_print_table_start();
php_info_print_table_header(2, "snowfrift support", "enabled");
php_info_print_table_row(2, "Version", PHP_SNOWDRIFT_VERSION);
php_info_print_table_end();
/* Remove comments if you have entries in php.ini */
DISPLAY_INI_ENTRIES();
}
/* }}} */
/* {{{ arginfo
*/
ZEND_BEGIN_ARG_INFO(arginfo_snowdrift_void, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_snowdrift_nextid, 0, 0, 1)
ZEND_ARG_INFO(0, wid)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_snowdrift_nextnumid, 0, 0, 1)
ZEND_ARG_INFO(0, num)
ZEND_ARG_INFO(0, wid)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ snowdrift_functions[]
*
* Every user visible function must have an entry in snowdrift_functions[].
*/
const zend_function_entry snowdrift_functions[] = {
PHP_FE_END /* Must be the last line in snowdrift_functions[] */
};
/* }}} */
/* {{{ snowdrift_method[]
*
* Every user visible function must have an entry in snowdrift_functions[].
*/
static const zend_function_entry snowdrift_methods[] = {
PHP_ME(snowdrift, NextId, arginfo_snowdrift_nextid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME(snowdrift, NextNumId, arginfo_snowdrift_nextnumid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END};
/* }}} */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(snowdrift)
{
INIT_CLASS_ENTRY(snowdrift_ce, "snowdrift", snowdrift_methods);
REGISTER_INI_ENTRIES();
zend_register_internal_class(&snowdrift_ce);
if (snowdrift_init() == FAILURE)
{
return FAILURE;
}
return SUCCESS;
}
/* }}} */
/* {{{ snowdrift_module_entry
*/
zend_module_entry snowdrift_module_entry = {
STANDARD_MODULE_HEADER,
"snowdrift",
snowdrift_functions,
PHP_MINIT(snowdrift),
PHP_MSHUTDOWN(snowdrift),
PHP_RINIT(snowdrift), /* Replace with NULL if there's nothing to do at
request start */
PHP_RSHUTDOWN(snowdrift), /* Replace with NULL if there's nothing to do at
request end */
PHP_MINFO(snowdrift),
PHP_SNOWDRIFT_VERSION,
STANDARD_MODULE_PROPERTIES};
/* }}} */
#ifdef COMPILE_DL_SNOWDRIFT
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(snowdrift)
#endif
<?php
declare(strict_types=1);
namespace SnowDrift;
use FFI;
use FFI\CData;
final class SnowFlake
{
private FFI $ffi;
private CData $flake;
private CData $pflake;
public function __construct(array $config = ['Method' => 1, 'BaseTime' => 0, 'WorkerId' => 1, 'WorkerIdBitLength' => 6, 'SeqBitLength' => 10, 'TopOverCostCount' => 2000])
{
$this->ffi = FFI::cdef(file_get_contents($this->getHeaders()), $this->getLibrary());
$this->flake = $this->ffi->new("struct snowflake");
foreach ($config as $name => $val) {
$this->flake->$name = $val;
}
$this->pflake = FFI::addr($this->flake);
$this->ffi->Config($this->pflake);
}
public function getFFI(): FFI
{
return $this->ffi;
}
public function getFlake(): CData
{
return $this->flake;
}
public function getPflake(): CData
{
return $this->pflake;
}
public function nextId(): int
{
return $this->ffi->NextId($this->pflake);
}
public function getHeaders(): string
{
return __DIR__ . '/src/snowflake/snowflake.h';
}
public function getLibrary(): ?string
{
return __DIR__ . '/src/snowflake/libsnow.so';
}
}
$total = 50000;
$snowflake = new SnowFlake(['Method' => 1, 'BaseTime' => 1577808000000, 'WorkerId' => 1, 'WorkerIdBitLength' => 1, 'SeqBitLength' => 10, 'TopOverCostCount' => 2000]);
$ffi = $snowflake->getFFI();
$pflake = $snowflake->getPflake();
// $res = [];
$start = microtime(true);
for ($i = 0; $i < $total; $i++) {
// $res[] = \SnowDrift::NextId();
\SnowDrift::NextId(2);
}
echo sprintf("扩展漂移算法,PHP循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL;
// $res = [];
$start = microtime(true);
foreach (\SnowDrift::NextNumId($total) as $val) {
// $res[] = $val;
}
echo sprintf("扩展漂移算法,C循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL;
// $res = [];
$start = microtime(true);
for ($i = 0; $i < $total; $i++) {
// $res[] = $ffi->NextId($pflake);
$ffi->NextId($pflake);
}
echo sprintf("FFI漂移算法,PHP循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL;
// $res = [];
$start = microtime(true);
$tmp = $ffi->NextNumId($pflake, $total);
for ($i = 0; $i < $total; $i++) {
// $res[] = $tmp[$i];
}
echo sprintf("FFI漂移算法,C循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL;
#Makefile
#自定义变量
CC = gcc
#编译选项,生成所有警告、不优化、采用c++11标准、输出调试信息、只编译并生成目标文件
CFLAGS = -Wall -O2 -g -c
FILE = ./test.c
#wildcard为Makefile模式匹配关键字,获取目标目录符合匹配模式的所有文件名
SRCS = $(FILE) $(wildcard ./snowflake/*.c)
#patsubst为Makefile模式替换关键字,查找字符串SRCS中按空格分开的单词,并将符合模式%.cpp的字符串全部替换成%.o
OBJS = $(patsubst ./%.c, ./%.o, $(SRCS))
EXES = test
RM = rm -f
#默认任务
default:
#默认任务要执行的命令,按上面的变量名替换为变量值后执行,前面必须有一个Tab符
$(MAKE) $(EXES)
#模式匹配,冒号前者为目标项,冒号后面为依赖项
$(EXES): $(OBJS)
#$^表示规则中所有的依赖项,$@表示规则中的目标
$(CC) $^ -lm -lpthread -o $@
# %模式自动匹配符
%.o: %.c
# $<表示规则中的第一个依赖项
$(CC) $(CFLAGS) $< -o $@
#伪目标,声明clean为伪目标或标签,为了避免该清理任务与文件名相同而被错识别
.PHONY: clean
clean:
#清理之前的目标文件,以便下次完整的重新编译
$(RM) $(OBJS) $(EXES)
\ No newline at end of file
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include "shm.h"
#ifdef MAP_ANON
int shm_alloc(struct shm *shm)
{
shm->addr = (void *)mmap(NULL, shm->size,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (shm->addr == NULL)
{
return -1;
}
return 0;
}
void shm_free(struct shm *shm)
{
if (shm->addr)
{
munmap((void *)shm->addr, shm->size);
}
}
#else
int shm_alloc(struct shm *shm)
{
int fd;
fd = open("/dev/zero", O_RDWR);
if (fd == -1)
{
return -1;
}
shm->addr = (void *)mmap(NULL, shm->size,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
if (shm->addr == NULL)
{
return -1;
}
return 0;
}
void shm_free(struct shm *shm)
{
if (shm->addr)
{
munmap((void *)shm->addr, shm->size);
}
}
#endif
#ifndef __SHM_H
#define __SHM_H
struct shm
{
void *addr;
size_t size;
};
int shm_alloc(struct shm *shm);
void shm_free(struct shm *shm);
#endif
#include <unistd.h>
#include <sys/time.h>
#include <stdlib.h>
#include "snowflake.h"
#include "spinlock.h"
#if defined(WIN32)
#include "windows.h"
#endif
static void EndOverCostAction(uint64_t useTimeTick, snowflake *flake);
static uint64_t NextOverCostId(snowflake *flake);
static uint64_t NextNormalId(snowflake *flake);
static uint64_t GetCurrentTimeTick(snowflake *flake);
static uint64_t GetNextTimeTick(snowflake *flake);
static uint64_t CalcId(snowflake *flake);
static uint64_t CalcTurnBackId(snowflake *flake);
int ncpu;
uint16_t spin = 2048;
uint32_t pid = 0;
void Config(snowflake *flake)
{
if (pid == 0)
{
pid = (uint32_t)getpid();
#if defined(WIN32)
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
ncpu = sysInfo.dwNumberOfProcessors;
#else
ncpu = sysconf(_SC_NPROCESSORS_ONLN);
#endif
if (ncpu <= 0)
{
ncpu = 1;
}
}
flake->WorkerIdBitLength = flake->WorkerIdBitLength == 0 ? 6 : flake->WorkerIdBitLength;
flake->SeqBitLength = flake->SeqBitLength == 0 ? 6 : flake->SeqBitLength;
flake->MaxSeqNumber = flake->MaxSeqNumber > 0 ? flake->MaxSeqNumber : (-1L << flake->SeqBitLength) ^ -1L;
flake->BaseTime = flake->BaseTime != 0 ? flake->BaseTime : 1577808000000;
flake->_TimestampShift = (uint8_t)(flake->WorkerIdBitLength + flake->SeqBitLength);
flake->_CurrentSeqNumber = flake->MinSeqNumber;
return;
}
void inline EndOverCostAction(uint64_t useTimeTick, snowflake *flake)
{
if (flake->_TermIndex > 10000)
{
flake->_TermIndex = 0;
}
}
uint64_t inline NextOverCostId(snowflake *flake)
{
uint64_t currentTimeTick = GetCurrentTimeTick(flake);
if (currentTimeTick > flake->_LastTimeTick)
{
EndOverCostAction(currentTimeTick, flake);
flake->_LastTimeTick = currentTimeTick;
flake->_CurrentSeqNumber = flake->MinSeqNumber;
flake->_IsOverCost = 0;
flake->_OverCostCountInOneTerm = 0;
flake->_GenCountInOneTerm = 0;
return CalcId(flake);
}
if (flake->_OverCostCountInOneTerm > flake->TopOverCostCount)
{
EndOverCostAction(currentTimeTick, flake);
flake->_LastTimeTick = GetNextTimeTick(flake);
flake->_CurrentSeqNumber = flake->MinSeqNumber;
flake->_IsOverCost = 0;
flake->_OverCostCountInOneTerm = 0;
flake->_GenCountInOneTerm = 0;
return CalcId(flake);
}
if (flake->_CurrentSeqNumber > flake->MaxSeqNumber)
{
flake->_LastTimeTick++;
flake->_CurrentSeqNumber = flake->MinSeqNumber;
flake->_IsOverCost = 1;
flake->_OverCostCountInOneTerm++;
flake->_GenCountInOneTerm++;
return CalcId(flake);
}
flake->_GenCountInOneTerm++;
return CalcId(flake);
}
uint64_t inline NextNormalId(snowflake *flake)
{
uint64_t currentTimeTick = GetCurrentTimeTick(flake);
if (currentTimeTick < flake->_LastTimeTick)
{
if (flake->_TurnBackTimeTick < 1)
{
flake->_TurnBackTimeTick = flake->_LastTimeTick - 1;
flake->_TurnBackIndex++;
if (flake->_TurnBackIndex > 4)
{
flake->_TurnBackIndex = 1;
}
}
return CalcTurnBackId(flake);
}
if (flake->_TurnBackTimeTick > 0)
{
flake->_TurnBackTimeTick = 0;
}
if (currentTimeTick > flake->_LastTimeTick)
{
flake->_LastTimeTick = currentTimeTick;
flake->_CurrentSeqNumber = flake->MinSeqNumber;
return CalcId(flake);
}
if (flake->_CurrentSeqNumber > flake->MaxSeqNumber)
{
flake->_TermIndex++;
flake->_LastTimeTick++;
flake->_CurrentSeqNumber = flake->MinSeqNumber;
flake->_IsOverCost = 1;
flake->_OverCostCountInOneTerm = 1;
flake->_GenCountInOneTerm = 1;
return CalcId(flake);
}
return CalcId(flake);
}
uint64_t inline GetCurrentTimeTick(snowflake *flake)
{
struct timeval t;
gettimeofday(&t, NULL);
return (uint64_t)((t.tv_sec * 1000 + t.tv_usec / 1000) - flake->BaseTime);
}
uint64_t inline GetNextTimeTick(snowflake *flake)
{
uint64_t tempTimeTicker = GetCurrentTimeTick(flake);
while (tempTimeTicker <= flake->_LastTimeTick)
{
tempTimeTicker = GetCurrentTimeTick(flake);
}
return tempTimeTicker;
}
uint64_t inline CalcId(snowflake *flake)
{
uint64_t result = (flake->_LastTimeTick << flake->_TimestampShift) + (flake->WorkerId << flake->SeqBitLength) + (flake->_CurrentSeqNumber);
flake->_CurrentSeqNumber++;
return result;
}
uint64_t inline CalcTurnBackId(snowflake *flake)
{
uint64_t result = (flake->_LastTimeTick << flake->_TimestampShift) + (flake->WorkerId << flake->SeqBitLength) + (flake->_TurnBackTimeTick);
flake->_TurnBackTimeTick--;
return result;
}
uint64_t inline NextSonwId(snowflake *flake)
{
uint64_t currentTimeTick = GetCurrentTimeTick(flake);
if (flake->_LastTimeTick == currentTimeTick)
{
flake->_CurrentSeqNumber++;
if (flake->_CurrentSeqNumber > flake->MaxSeqNumber)
{
flake->_CurrentSeqNumber = flake->MinSeqNumber;
currentTimeTick = GetNextTimeTick(flake);
}
}
else
{
flake->_CurrentSeqNumber = flake->MinSeqNumber;
}
flake->_LastTimeTick = currentTimeTick;
return (uint64_t)((currentTimeTick << flake->_TimestampShift) | (flake->WorkerId << flake->SeqBitLength) | flake->_CurrentSeqNumber);
}
uint64_t inline GetId(snowflake *flake)
{
return flake->Method == 1 ? (flake->_IsOverCost != 0 ? NextOverCostId(flake) : NextNormalId(flake)) : NextSonwId(flake);
}
uint64_t NextId(snowflake *flake)
{
spin_lock(&flake->_Lock, pid);
uint64_t id = GetId(flake);
spin_unlock(&flake->_Lock, pid);
return id;
}
uint64_t *NextNumId(snowflake *flake, uint32_t num)
{
uint64_t *arr = (uint64_t *)malloc(sizeof(uint64_t) * num);
spin_lock(&flake->_Lock, pid);
for (uint32_t i = 0; i < num; i++)
{
arr[i] = GetId(flake);
}
spin_unlock(&flake->_Lock, pid);
return arr;
}
#include <stdint.h>
typedef struct snowflake
{
uint8_t Method;
uint64_t BaseTime;
uint16_t WorkerId;
uint8_t WorkerIdBitLength;
uint8_t SeqBitLength;
uint32_t MaxSeqNumber;
uint32_t MinSeqNumber;
uint32_t TopOverCostCount;
uint8_t _TimestampShift;
uint32_t _CurrentSeqNumber;
int64_t _LastTimeTick;
int64_t _TurnBackTimeTick;
uint8_t _TurnBackIndex;
uint8_t _IsOverCost;
uint32_t _OverCostCountInOneTerm;
uint32_t _GenCountInOneTerm;
uint32_t _TermIndex;
volatile unsigned int _Lock;
} snowflake;
void Config(snowflake *flake);
uint64_t NextId(snowflake *flake);
uint64_t *NextNumId(snowflake *flake, uint32_t num);
\ No newline at end of file
#include <stdlib.h>
#include <sched.h>
#include "spinlock.h"
extern int ncpu;
extern int spin;
void spin_lock(atomic_t *lock, uint32_t pid)
{
int i, n;
for (;;)
{
if (*lock == 0 &&
__sync_bool_compare_and_swap(lock, 0, pid))
{
return;
}
if (ncpu > 1)
{
for (n = 1; n < spin; n <<= 1)
{
for (i = 0; i < n; i++)
{
__asm("pause");
}
if (*lock == 0 &&
__sync_bool_compare_and_swap(lock, 0, pid))
{
return;
}
}
}
sched_yield();
}
}
void spin_unlock(atomic_t *lock, uint32_t pid)
{
__sync_bool_compare_and_swap(lock, pid, 0);
}
#ifndef __SPINLOCK_H
#define __SPINLOCK_H
#include <stdint.h>
typedef volatile unsigned int atomic_t;
extern void spin_lock(atomic_t *lock, uint32_t pid);
extern void spin_unlock(atomic_t *lock, uint32_t pid);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <pthread.h>
#include <unistd.h>
#include "snowflake/snowflake.h"
#if defined(WIN32)
#include "windows.h"
#endif
#define THREAD 2
#define TOTAL 50000
static snowflake snowf = {1, 0, 1, 6, 6, 0, 0, 2000};
static snowflake *flake = &snowf;
uint64_t arr[TOTAL];
static uint64_t index = 0;
uint64_t compar(const void *a, const void *b)
{
return (*(uint64_t *)a - *(uint64_t *)b);
}
uint64_t containsDuplicate()
{
uint32_t i, j;
qsort(arr, TOTAL, sizeof(uint64_t), compar);
for (i = 0, j = 1; j < TOTAL; i++, j++)
{
if (arr[i] > 0 && arr[j] > 0 && arr[i] == arr[j])
{
return j;
}
}
return 0;
}
void run()
{
for (int i = 0; i < TOTAL / THREAD; i++)
{
arr[__sync_fetch_and_add(&index, 1)] = NextId(flake);
}
}
int main()
{
flake->Method = 1;
Config(flake);
pthread_t tid[THREAD];
struct timeval t_start, t_end;
while (1)
{
// clock_gettime(CLOCK_REALTIME, &t_start);
// for (int i = 0; i < THREAD; i++)
// {
// if (pthread_create(&tid[i], NULL, (void *)run, NULL) != 0)
// {
// printf("thread creation failed\n");
// exit(1);
// }
// }
// for (int i = 0; i < THREAD; i++)
// {
// pthread_join(tid[i], NULL); //等待线程结束
// }
// clock_gettime(CLOCK_REALTIME, &t_end);
// printf("%d 线程 %s,总共:%d,%lf ms\n", THREAD, flake->Method == 1 ? "漂移" : "传统", index, (double)(t_end.tv_nsec - t_start.tv_nsec) / 1000000.0);
// uint64_t re = containsDuplicate();
// if (re > 0)
// {
// printf("有重复数据:%ld,%ld\n", arr[re - 1], arr[re]);
// }
// else
// {
// printf("没有重复数据\n");
// }
// for (int i = 0; i < TOTAL; i++)
// {
// arr[i] = 0;
// }
// index = 0;
gettimeofday(&t_start, NULL);
for (int i = 0; i < TOTAL; i++)
{
NextId(flake);
}
gettimeofday(&t_end, NULL);
printf("单线程 %s,总共:%d,%lf ms\n", flake->Method == 1 ? "漂移" : "传统", TOTAL, (double)(t_end.tv_usec - t_start.tv_usec) / 1000.0);
sleep(1);
}
return 1;
}
\ No newline at end of file
--TEST--
Check for snowdrift presence
--SKIPIF--
<?php if (!extension_loaded("snowdrift")) print "skip"; ?>
--FILE--
<?php
echo "snowdrift extension is available";
/*
you can add regression tests for your extension here
the output of your test code has to be equal to the
text in the --EXPECT-- section below for the tests
to pass, differences between the output and the
expected text are interpreted as failure
see php7/README.TESTING for further information on
writing regression tests
*/
?>
--EXPECT--
snowdrift extension is available
--TEST--
Check for snowdrift unique
--SKIPIF--
<?php if (!extension_loaded("snowdrift")) print "skip"; ?>
--FILE--
<?php
$arr = [];
$max = 100000;
for ($i = 0; $i < $max; $i++) {
$id = SnowDrift::NextId();
$arr[$id] = '';
}
var_dump(count($arr));
?>
--EXPECT--
int(100000)
\ No newline at end of file
--TEST--
Check for snowdrift serial
--SKIPIF--
<?php if (!extension_loaded("snowdrift")) print "skip"; ?>
--FILE--
<?php
$arr = [];
$max = 1024;
for ($i = 0; $i < $max; $i++) {
$arr[$i] = SnowDrift::NextId();
}
var_dump(($arr[$max-1] - $arr[0]) == ($max-1));
?>
--EXPECT--
bool(true)
\ No newline at end of file
--TEST--
Check for snowdrift batch get unique
--SKIPIF--
<?php if (!extension_loaded("snowdrift")) print "skip"; ?>
--FILE--
<?php
$arr = [];
$max = 100000;
foreach (SnowDrift::NextNumId($max) as $id) {
$arr[$id] = '';
}
var_dump(count($arr));
?>
--EXPECT--
int(100000)
\ No newline at end of file
#!/bin/sh
phpize && ./configure && make clean && make && gcc -O2 -fPIC -shared -g src/snowflake/snowflake.c -o src/snowflake/libsnow.so
\ No newline at end of file
#!/bin/bash
TEST_DIR="`pwd`/tests/"
make test
for file in `find $TEST_DIR -name "*.diff" 2>/dev/null`
do
grep "\-\-XFAIL--" ${file/%diff/phpt} >/dev/null 2>&1
if [ $? -gt 0 ]
then
FAILS[${#FAILS[@]}]="$file"
fi
done
if [ ${#FAILS[@]} -gt 0 ]
then
for fail in "${FAILS[@]}"
do
sh -xc "cat $fail"
done
exit 1
else
exit 0
fi
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册