当前位置: 首页 > news >正文

安卓硬件加速hwui

安卓硬件加速
本文基于安卓11。

从 Android 3.0 (API 级别 11) 开始,Android 2D 渲染管道支持硬件加速,这意味着在 View 的画布上执行的所有绘图操作都使用 GPU。由于启用硬件加速所需的资源增加,你的应用程序将消耗更多内存。

软件绘制:

  1. Invalidate the hierarchy
  2. Draw the hierarchy

软件绘制在每次draw时都需要执行大量操作,比如一个Button位于另一个View上,当Button执行invalidate(),系统也重新绘制View尽管它什么都没有改变。

和硬件加速绘制:

  1. Invalidate the hierarchy
  2. Record and update display lists
  3. Draw the display lists

和软件绘制不同,硬件绘制不是立即执行绘制操作,而是UI线程把繁杂的绘制操作记录保存在display list当中,renderThread执行其中的绘制命令,对比软件绘制,硬件绘制只需要记录和更新dirty的View,也就是执行了invalidate()的View,其他的View可以重用display list中的记录。

其具体实现在hwui模块。
hwui UML:
hwui UML

1. RenderProxy

RenderProxy作为hwui提供给应用的功能接口,应用层通过ThreadedRenderer调用RenderProxy,RenderProxy内部持有RenderThread、CanvasContext、DrawFrameTask对象,CanvasContext拥有实际操作画面的能力,DrawFrameTask是对CanvasContext能力的封装。

ThreadedRenderer继承自HardwareRenderer,HardwareRenderer持有mNativeProxy变量,作为native层hwlib模块RenderProxy的引用。

RenderProxy提供了setSurface(), syncAndDrawFrame(), 等API供应用使用。

2. RenderThread

//ThreadedRenderer.java
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;choreographer.mFrameInfo.markDrawStart();updateRootDisplayList(view, callbacks);// register animating rendernodes which started animating prior to renderer// creation, which is typical for animators started prior to first drawif (attachInfo.mPendingAnimatingRenderNodes != null) {final int count = attachInfo.mPendingAnimatingRenderNodes.size();for (int i = 0; i < count; i++) {registerAnimatingRenderNode(attachInfo.mPendingAnimatingRenderNodes.get(i));}attachInfo.mPendingAnimatingRenderNodes.clear();// We don't need this anymore as subsequent calls to// ViewRootImpl#attachRenderNodeAnimator will go directly to us.attachInfo.mPendingAnimatingRenderNodes = null;}int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {Log.w("OpenGLRenderer", "Surface lost, forcing relayout");// We lost our surface. For a relayout next frame which should give us a new// surface from WindowManager, which hopefully will work.attachInfo.mViewRootImpl.mForceNextWindowRelayout = true;attachInfo.mViewRootImpl.requestLayout();}if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) {attachInfo.mViewRootImpl.invalidate();}
}

对于硬件加速的设备,绘制时启动新线程RenderThread负责绘制工作,RenderThread继承Thread类,但不是指Java层的ThreadedRenderer类,而是native层hwui的RenderThread,可以理解为Java层的ThreadedRenderer作为RenderThread的一个接口。

ThreadedRenderer的draw方法主要有两个步骤。

  1. 更新DisplayList,updateRootDisplayList

​ 更新DisplayList,分为LAYER_TYPE_SOFTWARE、LAYER_TYPE_HARDWARE两种情况:

  • LAYER_TYPE_SOFTWARE:drawBitmap,每个View缓存了Bitmap对象mDrawingCache。
  • LAYER_TYPE_HARDWARE: 更新DisplayList。
  1. 同步并提交绘制请求,syncAndDrawFrame:Syncs the RenderNode tree to the render thread and requests a frame to be drawn.

syncAndDrawFrame通过上述引用调用RenderProxy的syncAndDrawFrame方法,RenderProxy在RenderThread添加一个新的任务,执行DrawFrameTask的run()方法。

3. ReliableSurface

Surface初始化完成后,就可以把它传递给hwui模块的RenderProxy、CanvasContext、IRenderPipeline等对象使用。

//ViewRootImpl.java
private void performTraversals() {bool surfaceCreated = !hadSurface && mSurface.isValid();bool surfaceDestroyed = hadSurface && !mSurface.isValid();bool surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId())&& mSurface.isValid();if (surfaceCreated) {if (mAttachInfo.mThreadedRenderer != null) {hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);if (hwInitialized && (host.mPrivateFlags& View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {// Don't pre-allocate if transparent regions// are requested as they may not be neededmAttachInfo.mThreadedRenderer.allocateBuffers();}}} else if (surfaceDestroyed) {if (mAttachInfo.mThreadedRenderer != null &&mAttachInfo.mThreadedRenderer.isEnabled()) {mAttachInfo.mThreadedRenderer.destroy();}} else if ((surfaceReplaced|| surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged) {mAttachInfo.mThreadedRenderer.updateSurface(mSurface);}
}

ViewRootImpl判断surface状态是否是创建(surfaceCreated)、销毁(surfaceDestroyed)或者更新(surfaceReplaced|Changed),创建销毁和更新都是执行的同一个方法,销毁的时候setSurface(null),创建和更新setSurface(mSurface)。

mThreadedRenderer将mSurface通过RenderProxy传递给CanvasContext,更新其mNativeSurface变量std::unique_ptr<ReliableSurface> mNativeSurface;

ReliableSurface持有类变量ANativeWindow* mWindow;,是ANativeWindow的装饰者模式,ANativeWindow提供了扩展接口,使ReliableSurface可以在不改变现有对象结构的情况下,动态地向Surface对象添加功能,在其init()方法中通过添加拦截器,通过ANativeWindow扩展接口,将ReliableSurface的方法动态插入到Surface的接口中,通过拦截和管理ANativeWindow的操作,增强了对图形缓冲区的控制,从而提升系统的稳定性和渲染效果,例如检查缓冲区的状态是否合法、在操作失败时尝试恢复或提供警告、优化缓冲区的分配和释放逻辑等。

//ReliableSurface.cpp
void ReliableSurface::init() {int result = ANativeWindow_setCancelBufferInterceptor(mWindow, hook_cancelBuffer, this);LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d",result);result = ANativeWindow_setDequeueBufferInterceptor(mWindow, hook_dequeueBuffer, this);LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d",result);result = ANativeWindow_setQueueBufferInterceptor(mWindow, hook_queueBuffer, this);LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d",result);result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this);LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",result);result = ANativeWindow_setQueryInterceptor(mWindow, hook_query, this);LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set query interceptor: error = %d",result);
}

ANativeWindow提供了ANativeWindow_setCancelBufferInterceptor、ANativeWindow_setDequeueBufferInterceptor、ANativeWindow_setQueueBufferInterceptor等扩展接口,ReliableSurface分别用自己的hook_cancelBuffer、hook_dequeueBuffer、hook_queueBuffer等方法替代native层Surface的实现。

//ANativeWindow.cpp
int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,ANativeWindow_dequeueBufferInterceptor interceptor,void* data) {return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR, interceptor, data);
}

ANativeWindow提供的扩展接口。

//window.h
int     (*perform)(struct ANativeWindow* window,int operation, ... );

Surface作为ANativeWindow的接口实现,实现了perform方法。

//Surface.cpp
int Surface::perform(int operation, va_list args)
{int res = NO_ERROR;switch (operation) {case NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR:res = dispatchAddDequeueInterceptor(args);break;}return res;
}
int Surface::dispatchAddDequeueInterceptor(va_list args) {ANativeWindow_dequeueBufferInterceptor interceptor =va_arg(args, ANativeWindow_dequeueBufferInterceptor);void* data = va_arg(args, void*);std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);mDequeueInterceptor = interceptor;mDequeueInterceptorData = data;return NO_ERROR;
}

将ReliableSurface的hook_dequeueBuffer实现赋值给了Surface的mDequeueInterceptor变量,Surface在hook_dequeueBuffer时检查拦截器是否为空,如果不为空的话调用拦截器的操作。

//Surface.cpp
int Surface::hook_dequeueBuffer(ANativeWindow* window,ANativeWindowBuffer** buffer, int* fenceFd) {Surface* c = getSelf(window);{std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);if (c->mDequeueInterceptor != nullptr) {auto interceptor = c->mDequeueInterceptor;auto data = c->mDequeueInterceptorData;return interceptor(window, Surface::dequeueBufferInternal, data, buffer, fenceFd);}}return c->dequeueBuffer(buffer, fenceFd);
}

Surface的hook_dequeueBuffer在其构造函数中被绑定到ANativeWindow的dequeueBuffer函数指针上,从此dequeueBuffer都会调用ReliableSurface动态插入的hook_dequeueBuffer方法。

4. IRenderPipeline

前面说到应用层ViewRootImple实例化Surface对象通过RenderProxy接口传递给hwui模块,CanvasContext、IRenderPipeline对象需要Surface对象开始图形绘制工作,安卓支持两种渲染管线,OpenGL和Vulkan,这里是OpenGL的实现SkiaOpenGLPipeline,SkiaOpenGLPipeline通过使用跨平台的接口EGL管理OpenGL ES的上下文,可以看作是OpenGL ES提供给应用的接口。

setSurface(mSurface)最终SkiaOpenGLPipeline通过EglManager调用eglCreateWindowSurface,将窗口对象mSurface作为参数,EGL 创建一个新的 EGLSurface 对象,并将其连接到窗口对象的 BufferQueue 的生产方接口,此后,渲染到该 EGLSurface 会导致一个缓冲区离开队列、进行渲染,然后排队等待消费方使用。

setSurface(null)!mSurface.isValid()时调用,判断当前是否需要保留或者丢弃buffer,最终通过eglSurfaceAttrib改变EGL的buffer行为。

eglCreateWindowSurface只是创建了一个EGLSurface,还需要等到应用请求提交当前帧eglSwapBuffersWithDamageKHR发出绘制命令才能看到绘制的画面。

4.1 EGLSurface

关注一下EGLSurface是怎么创建的,它和Surface的关系是什么。

//SkiaOpenGLPipeline.cpp
bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) {if (surface) {mRenderThread.requireGlContext();auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace);if (!newSurface) {return false;}mEglSurface = newSurface.unwrap();}
}

传递ANativeWindow* surface给EglManager。

Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,ColorMode colorMode,sk_sp<SkColorSpace> colorSpace) {EGLSurface surface = eglCreateWindowSurface(mEglDisplay, wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs);return surface;
}

注意看这里surface对象被从ANativeWindow类型转换成了EGLNativeWindowType类型,EGLNativeWindowType被定义在EGL模块。

//EGL/eglplatform.h
#elif defined(__ANDROID__) || defined(ANDROID)
struct ANativeWindow;
struct egl_native_pixmap_t;typedef void*                           EGLNativeDisplayType;
typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
typedef struct ANativeWindow*           EGLNativeWindowType;
#elif defined(USE_OZONE)

EGL的eglplatform.h头文件定义了在Android平台,EGLNativeWindowType就是ANativeWindow*类型,安卓native层的Surface对象作为ANativeWindow的实现,被作为参数传递给eglCreateWindowSurface方法创建了EGLSurface对象,后续eglSwapBuffersWithDamageKHR交换缓冲区也是这个对象。

5. DrawFrameTask

//DrawFrameTask.cpp
void DrawFrameTask::run() {ATRACE_NAME("DrawFrame");bool canUnblockUiThread;bool canDrawThisFrame;{TreeInfo info(TreeInfo::MODE_FULL, *mContext);canUnblockUiThread = syncFrameState(info);canDrawThisFrame = info.out.canDrawThisFrame;if (mFrameCompleteCallback) {mContext->addFrameCompleteListener(std::move(mFrameCompleteCallback));mFrameCompleteCallback = nullptr;}}// Grab a copy of everything we needCanvasContext* context = mContext;std::function<void(int64_t)> callback = std::move(mFrameCallback);mFrameCallback = nullptr;// From this point on anything in "this" is *UNSAFE TO ACCESS*if (canUnblockUiThread) {unblockUiThread();}// Even if we aren't drawing this vsync pulse the next frame number will still be accurateif (CC_UNLIKELY(callback)) {context->enqueueFrameWork([callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });}if (CC_LIKELY(canDrawThisFrame)) {context->draw();} else {// wait on fences so tasks don't overlap next framecontext->waitOnFences();}if (!canUnblockUiThread) {unblockUiThread();}
}

UI线程(主线程)在RenderThread添加一个新的任务,执行DrawFrameTask的run()方法,UI线程阻塞等待RenderThread从UI线程同步完绘制所需要的信息之后,包括各个RenderNode的DisplayList、RenderProperties等属性,同步完判读是否能unblockUiThread发出信号,UI线程才能退出继续执行其他任务,重点关注context->draw();方法。

void CanvasContext::draw() {Frame frame = mRenderPipeline->getFrame();	// dequeueBuffersetPresentTime();SkRect windowDirty = computeDirtyRect(frame, &dirty);bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,&(profiler()));int64_t frameCompleteNr = getFrameNumber();waitOnFences();bool requireSwap = false;int error = OK;// queueBufferbool didSwap =mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
}

CanvasContext::draw执行一系列渲染操作,将绘制结果呈现到显示设备上。

  1. 获取帧。mRenderPipeline->getFrame(),作为图形队列中的生产者,getFrame通过gui模块的Surface对象dequeueBuffer申请GraphicBuffer,Surface对象由上文的setSurface方法传递过来。

  2. 计算脏区域(需要更新的区域)。computeDirtyRect(frame, &dirty)

  3. 绘制当前帧。mRenderPipeline->draw,向申请的GraphicBuffer中填充数据。

  4. 等待所有任务完成。waitOnFences

  5. 交换缓冲区并提交渲染结果。mRenderPipeline->swapBuffers,填充完成后通过gui模块的Surface对象queueBuffer将GraphicBuffer加入队列中。

5.1 draw

mRenderPipeline->draw

void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,const std::vector<sp<RenderNode>>& nodes, bool opaque,const Rect& contentDrawBounds, sk_sp<SkSurface> surface,const SkMatrix& preTransform) {// Initialize the canvas for the current frame, that might be a recording canvas if SKP// capture is enabled.SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);// draw all layers up frontrenderLayersImpl(layers, opaque);renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);endCapture(surface.get());if (CC_UNLIKELY(Properties::debugOverdraw)) {renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);}ATRACE_NAME("flush commands");surface->getCanvas()->flush();}
  1. tryCapture:Returns the canvas that records the drawing commands.
  2. renderFrameImpl:执行绘制命令。
  3. endCapture:Signal that the caller is done recording.
  4. surface->getCanvas()->flush();刷新fBytes缓存。

renderFrameImpl执行DisplayList记录的绘制操作,实际调用SkCanvas的绘制命令,例如canvas->drawRect(bounds, layerPaint),RecordingCanvas继承自SkCanvas,调用其onDrawRect方法:

void RecordingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {fDL->drawRect(rect, paint);
}

fDL是DisplayListData* fDL;对象

void DisplayListData::drawRect(const SkRect& rect, const SkPaint& paint) {this->push<DrawRect>(0, rect, paint);
}
template <typename T, typename... Args>
void* DisplayListData::push(size_t pod, Args&&... args) {size_t skip = SkAlignPtr(sizeof(T) + pod);SkASSERT(skip < (1 << 24));if (fUsed + skip > fReserved) {static_assert(SkIsPow2(SKLITEDL_PAGE), "This math needs updating for non-pow2.");// Next greater multiple of SKLITEDL_PAGE.fReserved = (fUsed + skip + SKLITEDL_PAGE) & ~(SKLITEDL_PAGE - 1);fBytes.realloc(fReserved);}SkASSERT(fUsed + skip <= fReserved);auto op = (T*)(fBytes.get() + fUsed);fUsed += skip;new (op) T{std::forward<Args>(args)...};op->type = (uint32_t)T::kType;op->skip = skip;return op + 1;
}

fBytes是SkAutoTMalloc<uint8_t> fBytes;,保存了所有绘制操作的内存空间,DisplayListData::push向其添加绘制操作,然后调用displayList->draw(canvas)读取保存的数据开始真正的绘制操作:

void DisplayListData::draw(SkCanvas* canvas) const {SkAutoCanvasRestore acr(canvas, false);this->map(draw_fns, canvas, canvas->getTotalMatrix());
}

draw_fn定义在"DisplayListOps.in"。

#define X(T)                                                    \[](const void* op, SkCanvas* c, const SkMatrix& original) { \((const T*)op)->draw(c, original);                      \},
static const draw_fn draw_fns[] = {
#include "DisplayListOps.in"
};
#undef X

DisplayListOps.in定义了所有的绘制方法,X(T)宏生成一个 lambda 表达式,将 const void* 类型的对象转换为 T 类型,并调用该类型的 draw 方法来执行绘制操作。

X(Flush)
X(Save)
X(Restore)...
X(Scale)
X(Translate)
X(ClipPath)
X(ClipRect)
X(ClipRRect)...
X(DrawPaint)
X(DrawBehind)
X(DrawPath)
X(DrawRect)...

例如DrawRect:

struct Op {uint32_t type : 8;uint32_t skip : 24;
};
struct DrawRect final : Op {static const auto kType = Type::DrawRect;DrawRect(const SkRect& rect, const SkPaint& paint) : rect(rect), paint(paint) {}SkRect rect;SkPaint paint;void draw(SkCanvas* c, const SkMatrix&) const { c->drawRect(rect, paint); }
};

DisplayListData::map是一个模板方法,遍历查找fBytes中是否存在Type::DrawRect,如果存在调用drawRect(rect, paint)

template <typename Fn, typename... Args>
inline void DisplayListData::map(const Fn fns[], Args... args) const {auto end = fBytes.get() + fUsed;for (const uint8_t* ptr = fBytes.get(); ptr < end;) {auto op = (const Op*)ptr;auto type = op->type;auto skip = op->skip;if (auto fn = fns[type]) {  // We replace no-op functions with nullptrsfn(op, args...);        // to avoid the overhead of a pointless call.}ptr += skip;}
}

5.2 swapBuffers

最终SkiaOpenGLPipeline通过EglManager调用eglSwapBuffersWithDamageKHR交换指定的脏区域的缓冲区内容提交当前帧,EGL 的工作机制是双缓冲模式,一个 Back Frame Buffer 和一个 Front Frame Buffer,正常绘制操作的目标都是 Back Frame Buffer,渲染完毕之后,调用eglSwapBuffersWithDamageKHR这个 API,会将绘制完毕的 Back Frame Buffer 与当前的 Front Frame Buffer 进行交换,buffer被EGL渲染完成。

6. 硬件绘制和软件绘制调用栈对比

应用作为图形生产者-消费者模型的一端,需要通过gui模块的IGraphicBufferProducer接口dequeueBufferqueueBuffer,可以看一下应用在启用硬件加速和软件绘制时的调用栈区别:

6.1 dequeueBuffer

  • 硬件绘制:
#03 pc 00000000000bacac  /system/lib64/libgui.so (android::Surface::hook_dequeueBuffer(ANativeWindow*, ANativeWindowBuffer**, int*)+324)
#04 pc 000000000000fd4c  /vendor/lib64/egl/eglSubDriverAndroid.so 
#05 pc 000000000000f830  /vendor/lib64/egl/eglSubDriverAndroid.so
#06 pc 0000000000236ff8  /vendor/lib64/egl/libGLESv2_adreno.so
#07 pc 000000000021c19c  /vendor/lib64/egl/libGLESv2_adreno.so
#08 pc 000000000001da4c  /system/lib64/libEGL.so (android::eglQuerySurfaceImpl(void*, void*, int, int*)+252)
#09 pc 0000000000222fa4  /system/lib64/libhwui.so (android::uirenderer::renderthread::EglManager::beginFrame(void*)+284) 
#10 pc 000000000021ef10  /system/lib64/libhwui.so (android::uirenderer::renderthread::CanvasContext::draw()+188) 
#11 pc 0000000000221598  /system/lib64/libhwui.so (_ZNSt3__110__function6__funcIZN7android10uirenderer12renderthread13DrawFrameTask11postAndWaitEvE3$_0NS_9allocatorIS6_EEFvvEEclEv$c303f2d2360db58ed70a2d0ac7ed911b+480) 
#12 pc 000000000020fd98  /system/lib64/libhwui.so (android::uirenderer::WorkQueue::process()+220) 
#13 pc 0000000000231050  /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+88) 
#14 pc 00000000000154d0  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+260) 
#15 pc 0000000000014d94  /system/lib64/libutils.so (thread_data_t::trampoline(thread_data_t const*)+412) 
#16 pc 00000000000afecc  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+64) 
#17 pc 0000000000050408  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) 
  • 软件绘制:
#00 pc 00000000000bce68  /system/lib64/libgui.so (android::Surface::dequeueBuffer(ANativeWindowBuffer**, int*)+1760) 
#01 pc 00000000000c1e50  /system/lib64/libgui.so (android::Surface::lock(ANativeWindow_Buffer*, ARect*)+140) 
#02 pc 00000000000f3688  /system/lib64/libandroid_runtime.so (android::nativeLockCanvas(_JNIEnv*, _jclass*, long, _jobject*, _jobject*)+484) 
#03 pc 000000000022bde0  /system/framework/arm64/boot-framework.oat (art_jni_trampoline+208) 25600fc)
#04 pc 00000000001337e8  /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+568) 
#05 pc 00000000001a8a94  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+228) 
#06 pc 000000000031831c  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+376) 
#07 pc 000000000030e648  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+996) 
#08 pc 000000000067f49c  /apex/com.android.art/lib64/libart.so (MterpInvokeStatic+548) 
#09 pc 000000000012d994  /apex/com.android.art/lib64/libart.so (mterp_op_invoke_static+20) 
#10 pc 0000000000237cb8  /system/framework/framework.jar (offset 0x124d000) (android.view.Surface.lockCanvas+36)
#11 pc 000000000067c7e4  /apex/com.android.art/lib64/libart.so (MterpInvokeVirtual+1520) 
#12 pc 000000000012d814  /apex/com.android.art/lib64/libart.so (mterp_op_invoke_virtual+20) 
#13 pc 0000000000254032  /system/framework/framework.jar (offset 0x124d000) (android.view.ViewRootImpl.drawSoftware+94)
#14 pc 0000000000682178  /apex/com.android.art/lib64/libart.so (MterpInvokeDirectRange+852) 
#15 pc 000000000012dc14  /apex/com.android.art/lib64/libart.so (mterp_op_invoke_direct_range+20) 
#16 pc 0000000000253f7a  /system/framework/framework.jar (offset 0x124d000) (android.view.ViewRootImpl.draw+1066)

6.1 queueBuffer

  • 硬件绘制
#03 pc 00000000000bb0a8  /system/lib64/libgui.so (android::Surface::hook_queueBuffer(ANativeWindow*, ANativeWindowBuffer*, int)+332) (BuildId: 6505069f09f7343ad7853cee7f4015df)
#04 pc 000000000000f3fc  /vendor/lib64/egl/eglSubDriverAndroid.so (BuildId: 015ff4a96331382a8b460005af9c9287)
#05 pc 00000000002373a0  /vendor/lib64/egl/libGLESv2_adreno.so (BuildId: c76f44e1ec1a1243f415aa2f2827d836)
#06 pc 000000000021dba8  /vendor/lib64/egl/libGLESv2_adreno.so (BuildId: c76f44e1ec1a1243f415aa2f2827d836)
#07 pc 000000000001e84c  /system/lib64/libEGL.so (android::eglSwapBuffersWithDamageKHRImpl(void*, void*, int*, int)+636) (BuildId: 0e7ed8d82a1b8513ffe9177265f2e685)
#08 pc 000000000001af90  /system/lib64/libEGL.so (eglSwapBuffersWithDamageKHR+72) (BuildId: 0e7ed8d82a1b8513ffe9177265f2e685)
#09 pc 00000000002231c4  /system/lib64/libhwui.so (android::uirenderer::renderthread::EglManager::swapBuffers(android::uirenderer::renderthread::Frame const&, SkRect const&)+176) (BuildId: ae332898f70c1bbe49e1ae88256cae7c)
#10 pc 0000000000216490  /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaOpenGLPipeline::swapBuffers(android::uirenderer::renderthread::Frame const&, bool, SkRect const&, android::uirenderer::FrameInfo*, bool*)+92) (BuildId: ae332898f70c1bbe49e1ae88256cae7c)
#11 pc 000000000021f0dc  /system/lib64/libhwui.so (android::uirenderer::renderthread::CanvasContext::draw()+648) (BuildId: ae332898f70c1bbe49e1ae88256cae7c)
#12 pc 0000000000221598  /system/lib64/libhwui.so (_ZNSt3__110__function6__funcIZN7android10uirenderer12renderthread13DrawFrameTask11postAndWaitEvE3$_0NS_9allocatorIS6_EEFvvEEclEv$c303f2d2360db58ed70a2d0ac7ed911b+480) (BuildId: ae332898f70c1bbe49e1ae88256cae7c)
#13 pc 000000000020fd98  /system/lib64/libhwui.so (android::uirenderer::WorkQueue::process()+220) (BuildId: ae332898f70c1bbe49e1ae88256cae7c)
#14 pc 0000000000231050  /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+88) (BuildId: ae332898f70c1bbe49e1ae88256cae7c)
#15 pc 00000000000154d0  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+260) (BuildId: 5d6af74124211886d954d61c96514a46)
#16 pc 0000000000014d94  /system/lib64/libutils.so (thread_data_t::trampoline(thread_data_t const*)+412) (BuildId: 5d6af74124211886d954d61c96514a46)
#17 pc 00000000000afecc  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+64) (BuildId: 6a9c4bd651deaf319e05cb1a7346047f)
#18 pc 0000000000050408  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 6a9c4bd651deaf319e05cb1a7346047f)
  • 软件绘制:
#00 pc 00000000000bde88  /system/lib64/libgui.so (android::Surface::queueBuffer(ANativeWindowBuffer*, int)+2316) 
#01 pc 00000000000c24c8  /system/lib64/libgui.so (android::Surface::unlockAndPost()+108) 
#02 pc 00000000000f3848  /system/lib64/libandroid_runtime.so (android::nativeUnlockCanvasAndPost(_JNIEnv*, _jclass*, long, _jobject*)+160) 
#03 pc 00000000001f5df4  /system/framework/arm64/boot-framework.oat (art_jni_trampoline+180) 25600fc)
#04 pc 00000000001337e8  /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+568) 
#05 pc 00000000001a8a94  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+228) 
#06 pc 000000000031831c  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+376) 
#07 pc 000000000030e648  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+996) 
#08 pc 000000000067f49c  /apex/com.android.art/lib64/libart.so (MterpInvokeStatic+548) 
#09 pc 000000000012d994  /apex/com.android.art/lib64/libart.so (mterp_op_invoke_static+20) 
#10 pc 000000000023896a  /system/framework/framework.jar (offset 0x124d000) (android.view.Surface.unlockSwCanvasAndPost+134)
#11 pc 000000000067ee60  /apex/com.android.art/lib64/libart.so (MterpInvokeDirect+1248) 
#12 pc 000000000012d914  /apex/com.android.art/lib64/libart.so (mterp_op_invoke_direct+20) 
#13 pc 00000000002388b8  /system/framework/framework.jar (offset 0x124d000) (android.view.Surface.unlockCanvasAndPost+32)
#14 pc 000000000067c7e4  /apex/com.android.art/lib64/libart.so (MterpInvokeVirtual+1520) 
#15 pc 000000000012d814  /apex/com.android.art/lib64/libart.so (mterp_op_invoke_virtual+20) 
#16 pc 00000000002540c6  /system/framework/framework.jar (offset 0x124d000) (android.view.ViewRootImpl.drawSoftware+242)
#17 pc 0000000000682178  /apex/com.android.art/lib64/libart.so (MterpInvokeDirectRange+852) 
#18 pc 000000000012dc14  /apex/com.android.art/lib64/libart.so (mterp_op_invoke_direct_range+20) 
#19 pc 0000000000253f7a  /system/framework/framework.jar (offset 0x124d000) (android.view.ViewRootImpl.draw+1066)

相关文章:

安卓硬件加速hwui

安卓硬件加速 本文基于安卓11。 从 Android 3.0 (API 级别 11) 开始&#xff0c;Android 2D 渲染管道支持硬件加速&#xff0c;这意味着在 View 的画布上执行的所有绘图操作都使用 GPU。由于启用硬件加速所需的资源增加&#xff0c;你的应用程序将消耗更多内存。 软件绘制&am…...

【Bluedroid】HFP连接流程源码分析(二)

接上一篇【Bluedroid】HFP连接流程源码分析&#xff08;一&#xff09;-CSDN博客分析。本篇主要围绕RFCOMM Connect 与 RFCOMM UA Frame 的处理流程来展开分析。 RFCOMM Connect RFCOMM&#xff08;Radio Frequency Communication&#xff09;作为蓝牙协议栈的关键部分&#…...

基于文件系统分布式锁原理

分布式锁&#xff1a;在一个公共的存储服务上打上一个标记&#xff0c;如Redis的setnx命令&#xff0c;是先到先得方式获得锁&#xff0c;ZooKeeper有点像下面的demo,比较大小的方式判决谁获得锁。 package com.ldj.mybatisflex.demo;import java.util.*; import java.util.co…...

java语法知识(二)

1. class文件可以直接拖动到idea中&#xff0c;显示源码。 2.idea快捷键&#xff1a; sout : System.out.println 输出内容.sout :---》 System.out.println(输出内容); psvm: public static void main() 格式化&#xff1a;ctrl altL 复制粘贴&#xff1a;ctrld 3.注释…...

基于Piquasso的光量子计算机的模拟与编程

一、引言 在科技飞速发展的当下,量子计算作为前沿领域,正以前所未有的态势蓬勃崛起。它凭借独特的量子力学原理,为解决诸多经典计算难以攻克的复杂问题提供了全新路径。从优化物流配送网络,以实现资源高效调配,到药物分子结构的精准模拟,加速新药研发进程;从金融风险的…...

导出文件,能够导出但是文件打不开

背景&#xff1a; 在项目开发中&#xff0c;对于列表的查询&#xff0c;而后会有导出功能&#xff0c;这里导出的是一个excell表格。实现了两种&#xff0c;1.导出的文件&#xff0c;命名是前端传输过去的&#xff1b;2.导出的文件&#xff0c;命名是根据后端返回的文件名获取的…...

【动手学电机驱动】STM32-FOC(4)STM32之UART 串口通信

STM32-FOC&#xff08;1&#xff09;STM32 电机控制的软件开发环境 STM32-FOC&#xff08;2&#xff09;STM32 导入和创建项目 STM32-FOC&#xff08;3&#xff09;STM32 三路互补 PWM 输出 STM32-FOC&#xff08;4&#xff09;STM32之UART 串口通信 STM32-FOC&#xff08;6&am…...

RabbitMQ 高可用方案:原理、构建与运维全解析

文章目录 前言&#xff1a;1 集群方案的原理2 RabbitMQ高可用集群相关概念2.1 设计集群的目的2.2 集群配置方式2.3 节点类型 3 集群架构3.1 为什么使用集群3.2 集群的特点3.3 集群异常处理3.4 普通集群模式3.5 镜像集群模式 前言&#xff1a; 在实际生产中&#xff0c;RabbitM…...

Center Loss 和 ArcFace Loss 笔记

一、Center Loss 1. 定义 Center Loss 旨在最小化类内特征的离散程度&#xff0c;通过约束样本特征与其类别中心之间的距离&#xff0c;提高类内特征的聚合性。 2. 公式 对于样本 xi​ 和其类别yi​&#xff0c;Center Loss 的公式为&#xff1a; xi​: 当前样本的特征向量&…...

深度解读微软Speech服务:让语音识别走进现实

大家好&#xff0c;今天我们来探讨一个激动人心的技术话题&#xff1a;微软的语音识别服务如何为我们提供强大的语音识别解决方案&#xff0c;特别是在电话录音中识别出不同的说话人。 场景描绘 想象一下&#xff0c;你有一段电话录音&#xff0c;并需要将其中的多个说话人区分…...

第21篇 基于ARM A9处理器用汇编语言实现中断<三>

Q&#xff1a;怎样编写ARM A9处理器汇编语言代码配置按键端口产生中断&#xff1f; A&#xff1a;使用Intel Monitor Program创建中断程序时&#xff0c;Linker Section Presets下拉菜单中需选择Exceptions。主程序在.vectors代码段为ARM处理器设置异常向量表&#xff0c;在…...

专题 - STM32

基础 基础知识 STM所有产品线&#xff08;列举型号&#xff09;&#xff1a; STM产品的3内核架构&#xff08;列举ARM芯片架构&#xff09;&#xff1a; STM32的3开发方式&#xff1a; STM32的5开发工具和套件&#xff1a; 若要在电脑上直接硬件级调试STM32设备&#xff0c;则…...

极客说|Azure AI Agent Service 结合 AutoGen/Semantic Kernel 构建多智能体解决⽅案

作者&#xff1a;卢建晖 - 微软高级云技术布道师 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&am…...

【C++指南】模板 深度解析

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 目录 1. 引言 2. 模板的基本概念 3. 函数模板 3.1 定义和语法 3.2 函数模板实例化 3.3 隐式实例化 …...

【traefik】forwadAuth中间件跨namespace请求的问题

前情提要 - fowardAuth鉴权中间件的使用&#xff1a; 【traefik】使用forwardAuth中间件做网关层的全局鉴权 1. 问题 我的 traefik-ingress-controller 所在 namespace: traefik 业务服务所在 namespace: apps 路由与 forwardAuth 中间件配置如下&#xff1a; # 路由 apiV…...

【25考研】西南交通大学软件工程复试攻略!

一、复试内容 复试对考生的既往学业情况、外语听说交流能力、专业素质和科研创新能力&#xff0c;以及综合素质和一贯表现等进行全面考查,主要考核内容包括思想政治素质和道德品质、外语听说能力、专业素质和能力&#xff0c;综合素质及能力。考核由上机考试和面试两部分组成&a…...

在 Safari 浏览器中,快速将页面恢复到 100% 缩放(也就是默认尺寸)Command (⌘) + 0 (零)

在 Safari 浏览器中&#xff0c;没有一个专门的快捷键可以将页面恢复到默认的缩放比例。 但是&#xff0c;你可以使用以下两种方法快速将页面恢复到 100% 缩放&#xff08;也就是默认尺寸&#xff09;&#xff1a; 方法一&#xff1a;使用快捷键 (最常用) Command (⌘) 0 (零…...

linux的大内核锁与顺序锁

大内核锁 Linux大内核锁&#xff08;Big Kernel Lock&#xff0c;BKL&#xff09;是Linux内核中的一种锁机制&#xff0c;用于保护内核资源&#xff0c;以下是关于它的详细介绍&#xff1a; 概念与作用 大内核锁是一种全局的互斥锁&#xff0c;在同一时刻只允许一个进程访问…...

CVE-2025-22777 (CVSS 9.8):WordPress | GiveWP 插件的严重漏洞

漏洞描述 GiveWP 插件中发现了一个严重漏洞&#xff0c;该插件是 WordPress 最广泛使用的在线捐赠和筹款工具之一。该漏洞的编号为 CVE-2025-22777&#xff0c;CVSS 评分为 9.8&#xff0c;表明其严重性。 GiveWP 插件拥有超过 100,000 个活跃安装&#xff0c;为全球无数捐赠平…...

牛客周赛 Round 76题解

小红出题 思路&#xff1a;我们发现&#xff0c;每七天可以获得15元&#xff0c;那么我们可以对7取模&#xff0c;看能有多少7的倍数&#xff0c;然后剩下的就是看是否超过5&#xff0c;超过5就直接15&#xff0c;否则加上天数*3 #include<bits/stdc.h> using namespace…...

【ARM】MDK如何将变量存储到指定内存地址

1、 文档目标 通过MDK的工程配置&#xff0c;将指定的变量存储到指定的内存地址上。 2、 问题场景 在项目工程的开发过程中&#xff0c;对于flash要进行分区&#xff0c;需要规划出一个特定的内存区域来存储变量。 3、软硬件环境 1&#xff09;、软件版本&#xff1a;MDK 5.…...

解决在arm架构下的欧拉操作系统mysql8.4.2源码安装

目标&#xff1a;在欧拉的22.03 (LTS-SP4)版本操作系统&#xff0c;cpu的架构为ARM&#xff0c;源码安装mysql-8.4.2。 1.查看操作系统 # cat /etc/os-release NAME"openEuler" VERSION"22.03 (LTS-SP4)"# uname -i aarch642.mysql下载地址 mysql的下载…...

SpringAop

SpringAop aop定义核心概念aop基础实现执行流程 aop进阶通知类型切入点表达式的抽取通知的执行顺序切入点表达式execution方式实现annotation注解方式实现示例 笔记链接 aop定义 AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09…...

C++内存泄露排查

内存泄漏是指程序动态分配的内存未能及时释放&#xff0c;导致系统内存逐渐耗尽&#xff0c;最终可能造成程序崩溃或性能下降。在C中&#xff0c;内存泄漏通常发生在使用new或malloc等分配内存的操作时&#xff0c;但没有正确地使用delete或free来释放这块内存。 在日常开发过程…...

Cesium小知识:pointPrimitive collection 详解

Cesium.PointPrimitiveCollection 是 Cesium 中用于高效管理和渲染大量点(points)的一个类。它允许你创建和管理大量的 PointPrimitive 实例,这些实例可以用来表示地理空间中的点数据,如传感器位置、车辆位置、兴趣点等。与直接使用 Cesium.Entity 相比,PointPrimitiveCol…...

从 Conda 到 Pip-tools:Python 依赖管理全景探索20250113

从 Conda 到 Pip-tools&#xff1a;Python 依赖管理全景探索 引言 在 Python 开发中&#xff0c;依赖管理是一个"常见但复杂"的问题&#xff1a;一次简单的版本冲突可能让团队调试数小时&#xff1b;一次不受控的依赖升级可能让生产环境瘫痪。随着项目规模的增加和…...

浅谈云计算01 | 云计算服务的特点

在当今数字化时代&#xff0c;云计算作为一种强大的技术解决方案&#xff0c;正逐渐改变着企业和个人对信息技术的使用方式。本文将详细探讨云计算的五个主要特点&#xff0c;包括按需自助服务、广泛的网络接入、资源池化、快速弹性伸缩以及可计量服务。 一、按需自助服务 云…...

2025年,华为认证HCIA、HCIP、HCIE 该如何选择?

眼看都到 2025 年啦&#xff0c;华为认证还吃香不&#xff1f; 把这问题摆在每个网络工程师跟前&#xff0c;答案可没那么容易说清楚。 到底考不考它值当不值当&#xff0c;重点在于您自己的职业规划&#xff0c;还有对行业走向的领会。 2025 年华为认证仍然值得一考&#…...

使用Selenium进行网页自动化测试

在使用Selenium进行网页自动化测试时&#xff0c;获取网络请求数据&#xff08;即network数据&#xff09;并不直接由Selenium库提供。Selenium主要用于与网页内容进行交互&#xff08;如点击、输入文本、获取页面元素等&#xff09;&#xff0c;但它本身不拦截或记录网络请求。…...

Linux 下 mtrace 的详细介绍

在 Linux 系统中&#xff0c;内存管理是操作系统的一项重要任务&#xff0c;而内存泄漏&#xff08;Memory Leak&#xff09;是开发过程中常见且棘手的问题之一。为了帮助开发者追踪和调试内存泄漏问题&#xff0c;mtrace 提供了一种有效的方式来检测和分析内存的分配与释放情况…...

【DB-GPT】开启数据库交互新篇章的技术探索与实践

一、引言&#xff1a;AI原生数据应用开发的挑战与机遇 在数字化转型的浪潮中&#xff0c;企业对于智能化应用的需求日益增长。然而&#xff0c;传统的数据应用开发方式面临着诸多挑战&#xff0c;如技术栈复杂、开发周期长、成本高昂、难以维护等。这些问题限制了智能化应用的…...

深入 Flutter 和 Compose 在 UI 渲染刷新时 Diff 实现对比

众所周知&#xff0c;不管是什么框架&#xff0c;在前端 UI 渲染时&#xff0c;都会有构造出一套相关的渲染树&#xff0c;并且在 UI 更新时&#xff0c;为了尽可能提高性能&#xff0c;一般都只会进行「差异化」更新&#xff0c;而不是对整个 UI Tree 进行刷新&#xff0c;所以…...

Android 网络层相关介绍

关注 Android 默认支持的网络管理行为,默认支持的网络服务功能。 功能术语 术语缩写全称释义DHCPv6Dynamic Host Configuration Protocol for IPv6动态主机配置协议的第六版,用于在IPv6网络中动态分配IP地址和其他网络配置参数。DNS Domain Name System域名系统。LLALink-Loc…...

ThreeJs开发环境安装与首个DEMO

安装开发环境 我这边使用的JetBrain的WebStorm&#xff0c;咨询过很多其他开发从业者&#xff0c;普遍使用vscode的比较多。但是考虑到vscode涉及到不少插件安装和IDE配置&#xff0c;作为傻瓜式入门&#xff0c;我这边采用WebStorm。 下载地址&#xff1a; WebStorm: The J…...

【Vim Masterclass 笔记09】S06L22:Vim 核心操作训练之 —— 文本的搜索、查找与替换操作(第一部分)

文章目录 S06L22 Search, Find, and Replace - Part One1 从光标位置起&#xff0c;正向定位到当前行的首个字符 b2 从光标位置起&#xff0c;反向查找某个字符3 重复上一次字符查找操作4 定位到目标字符的前一个字符5 单字符查找与 Vim 命令的组合6 跨行查找某字符串7 Vim 的增…...

js:根据后端返回数据的最大值进行计算然后设置这个最大值为百分之百,其他的值除这个最大值

问&#xff1a; 现在tabData.value 接收到了后端返回的数据&#xff0c; [{text:人力,percentage&#xff1a;‘90’}&#xff0c;{text:物品,percentage&#xff1a;‘20’}&#xff0c;{text:物理,percentage&#xff1a;‘50’}&#xff0c;{text:服务,percentage&#xff…...

线形回归与小批量梯度下降实例

1、准备数据集 import numpy as np import matplotlib.pyplot as pltfrom torch.utils.data import DataLoader from torch.utils.data import TensorDataset######################################################################### #################准备若干个随机的x和…...

【数学】概率论与数理统计(三)

文章目录 [toc] 随机变量的概念随机事件数量化随机变量 离散型随机变量及其概率分布随机变量的分类离散型随机变量离散型随机变量的常见分布两点分布二项分布泊松分布泊松定理证明 泊松分布 超几何分布几何分布 连续型随机变量及其概率分布连续型随机变量零概率事件几乎必然发生…...

如何在 Linux、MacOS 以及 Windows 中打开控制面板

控制面板不仅仅是一系列图标和菜单的集合&#xff1b;它是通往优化个人计算体验的大门。通过它&#xff0c;用户可以轻松调整从外观到性能的各种参数&#xff0c;确保他们的电脑能够完美地适应自己的需求。无论是想要提升系统安全性、管理硬件设备&#xff0c;还是简单地改变桌…...

《AI赋能鸿蒙Next,开启智能关卡设计新时代》

在游戏开发领域&#xff0c;关卡设计是至关重要的一环&#xff0c;它直接影响着玩家的游戏体验和沉浸感。而随着人工智能技术的飞速发展&#xff0c;结合鸿蒙Next系统的强大功能&#xff0c;为游戏的智能关卡设计带来了全新的思路和方法。 利用AI学习玩家行为模式 在鸿蒙Next…...

Safari浏览器上ico图标显示不出来,怎么解决?

Safari浏览器上ico图标显示不出来&#xff0c;怎么解决&#xff1f; 如果Safari浏览器上ico图标显示不出来了&#xff0c;如下图&#xff0c;该图标显示为灰色。 可以关闭Safari浏览器&#xff0c;并清除历史记录&#xff0c;就可以解决啦。 另外&#xff0c;如果多个网站这…...

Java Bean Validation 不适用Spring的情况下自定义validation注解

Java Bean Validation&#xff08;也称为 JSR 380&#xff0c;为 Bean Validation 2.0 规范&#xff09;提供了一套基本的注解&#xff0c;用于定义和验证 Java Bean 的属性。例如&#xff1a; NotNull&#xff1a;属性不能为空 Size&#xff1a;字符串、集合或数组的大小有约…...

【算法学习笔记】30:埃氏筛(Sieve of Eratosthenes)和线性筛(Linear Sieve)

测试题目&#xff1a;AcWing 868. 筛质数 埃氏筛&#xff08;Sieve of Eratosthenes&#xff09; 如果 i i i是素数&#xff0c;每次把 i i i的倍数都筛掉&#xff0c;存在重复筛选&#xff0c;时间复杂度 n ⋅ l o g ( l o g n ) n \cdot log(logn) n⋅log(logn)。 #includ…...

风控业务——评分模型

本文主要讲述了金融机构风控模型的重要性及其应用。首先&#xff0c;开头概述了风控模型的整体建模流程&#xff0c;包括特征工程和建模方法。接着&#xff0c;本文强调了贷前、贷中、贷后三个阶段中风控模型的应用&#xff0c;如信用评分、行为评分和催收评分。同时还提到了信…...

jupyter notebook练手项目:线性回归——学习时间与成绩的关系

线性回归——学习时间与学习成绩的关系 第1步&#xff1a;导入工具库 pandas——数据分析库&#xff0c;提供了数据结构&#xff08;如DataFrame和Series&#xff09;和数据操作方法&#xff0c;方便对数据集进行读取、清洗、转换等操作。 matplotlib——绘图库&#xff0c;p…...

DDD - 微服务设计与领域驱动设计实战(上)_统一建模语言及事件风暴会议

文章目录 Pre概述业务流程需求分析的困境统一语言建模事件风暴会议什么是事件风暴&#xff08;Event Storming&#xff09;事件风暴会议 总结 Pre DDD - 软件退化原因及案例分析 DDD - 如何运用 DDD 进行软件设计 DDD - 如何运用 DDD 进行数据库设计 DDD - 服务、实体与值对…...

《自动驾驶与机器人中的SLAM技术》ch7:基于 ESKF 的松耦合 LIO 系统

目录 基于 ESKF 的松耦合 LIO 系统 1 坐标系说明 2 松耦合 LIO 系统的运动和观测方程 3 松耦合 LIO 系统的数据准备 3.1 CloudConvert 类 3.2 MessageSync 类 4 松耦合 LIO 系统的主要流程 4.1 IMU 静止初始化 4.2 ESKF 之 运动过程——使用 IMU 预测 4.3 使用 IMU 预测位姿进…...

day07_Spark SQL

文章目录 day07_Spark SQL课程笔记一、今日课程内容二、Spark SQL函数定义&#xff08;掌握&#xff09;1、窗口函数2、自定义函数背景2.1 回顾函数分类标准:SQL最开始是_内置函数&自定义函数_两种 2.2 自定义函数背景 3、Spark原生自定义UDF函数3.1 自定义函数流程&#x…...

【LC】2270. 分割数组的方案数

题目描述&#xff1a; 给你一个下标从 0 开始长度为 n 的整数数组 nums 。 如果以下描述为真&#xff0c;那么 nums 在下标 i 处有一个 合法的分割 &#xff1a; 前 i 1 个元素的和 大于等于 剩下的 n - i - 1 个元素的和。下标 i 的右边 至少有一个 元素&#xff0c;也就是…...

Docker 容器通信的网络模式详解

Docker 的网络模式是容器化技术中非常重要的一部分&#xff0c;它决定了容器之间以及容器与外部世界如何通信。Docker 提供了多种网络模式&#xff0c;每种模式都有其特定的使用场景和优势。本文将深入探讨 Docker 的网络模式&#xff0c;包括桥接模式、主机模式、覆盖网络模式…...