Skip to content

Commit 0da7c1d

Browse files
fix: Configure environment paths for Ray worker compatibility
Use PYTHONPATH and PATH env vars to ensure Ray workers can access packages installed by uv sync, maintaining consistent uv usage across all make targets while supporting subprocess tools. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 2068303 commit 0da7c1d

23 files changed

+2795
-0
lines changed

.github/workflows/unit_tests.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ jobs:
3636
- name: Install dependencies
3737
run: make install-python-dependencies-ci
3838
- name: Test Python
39+
env:
40+
PYTHONPATH: "/home/runner/work/feast/feast/.venv/lib/python${{ matrix.python-version }}/site-packages:$PYTHONPATH"
41+
PATH: "/home/runner/work/feast/feast/.venv/bin:$PATH"
3942
run: make test-python-unit
4043
- name: Minimize uv cache
4144
run: uv cache prune --ci

feast_profile_demo/.gitignore

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*.pyo
5+
*.pyd
6+
7+
# C extensions
8+
*.so
9+
10+
# Distribution / packaging
11+
.Python
12+
env/
13+
venv/
14+
ENV/
15+
env.bak/
16+
venv.bak/
17+
*.egg-info/
18+
dist/
19+
build/
20+
.venv
21+
22+
# Pytest
23+
.cache
24+
*.cover
25+
*.log
26+
.coverage
27+
nosetests.xml
28+
coverage.xml
29+
*.hypothesis/
30+
*.pytest_cache/
31+
32+
# Jupyter Notebook
33+
.ipynb_checkpoints
34+
35+
# IDEs and Editors
36+
.vscode/
37+
.idea/
38+
*.swp
39+
*.swo
40+
*.sublime-workspace
41+
*.sublime-project
42+
43+
# OS generated files
44+
.DS_Store
45+
Thumbs.db

feast_profile_demo/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Feast Quickstart
2+
If you haven't already, check out the quickstart guide on Feast's website (http://docs.feast.dev/quickstart), which
3+
uses this repo. A quick view of what's in this repository's `feature_repo/` directory:
4+
5+
* `data/` contains raw demo parquet data
6+
* `feature_repo/feature_definitions.py` contains demo feature definitions
7+
* `feature_repo/feature_store.yaml` contains a demo setup configuring where data sources are
8+
* `feature_repo/test_workflow.py` showcases how to run all key Feast commands, including defining, retrieving, and pushing features.
9+
10+
You can run the overall workflow with `python test_workflow.py`.
11+
12+
## To move from this into a more production ready workflow:
13+
> See more details in [Running Feast in production](https://docs.feast.dev/how-to-guides/running-feast-in-production)
14+
15+
1. First: you should start with a different Feast template, which delegates to a more scalable offline store.
16+
- For example, running `feast init -t gcp`
17+
or `feast init -t aws` or `feast init -t snowflake`.
18+
- You can see your options if you run `feast init --help`.
19+
2. `feature_store.yaml` points to a local file as a registry. You'll want to setup a remote file (e.g. in S3/GCS) or a
20+
SQL registry. See [registry docs](https://docs.feast.dev/getting-started/concepts/registry) for more details.
21+
3. This example uses a file [offline store](https://docs.feast.dev/getting-started/components/offline-store)
22+
to generate training data. It does not scale. We recommend instead using a data warehouse such as BigQuery,
23+
Snowflake, Redshift. There is experimental support for Spark as well.
24+
4. Setup CI/CD + dev vs staging vs prod environments to automatically update the registry as you change Feast feature definitions. See [docs](https://docs.feast.dev/how-to-guides/running-feast-in-production#1.-automatically-deploying-changes-to-your-feature-definitions).
25+
5. (optional) Regularly scheduled materialization to power low latency feature retrieval (e.g. via Airflow). See [Batch data ingestion](https://docs.feast.dev/getting-started/concepts/data-ingestion#batch-data-ingestion)
26+
for more details.
27+
6. (optional) Deploy feature server instances with `feast serve` to expose endpoints to retrieve online features.
28+
- See [Python feature server](https://docs.feast.dev/reference/feature-servers/python-feature-server) for details.
29+
- Use cases can also directly call the Feast client to fetch features as per [Feature retrieval](https://docs.feast.dev/getting-started/concepts/feature-retrieval)

feast_profile_demo/__init__.py

Whitespace-only changes.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Feast Performance Profiling Suite
2+
3+
## Overview
4+
5+
This repository contains a comprehensive performance profiling suite for Feast's feature serving infrastructure. The profiling tools help identify bottlenecks in FeatureStore operations, FastAPI server performance, and component-level inefficiencies.
6+
7+
## Files Created
8+
9+
### Core Profiling Scripts
10+
11+
1. **`profiling_utils.py`** - Shared utilities for cProfile management, timing, memory tracking
12+
2. **`profile_feature_store.py`** - Direct FeatureStore.get_online_features() profiling
13+
3. **`profile_feature_server.py`** - FastAPI server endpoint profiling (requires requests, aiohttp)
14+
4. **`profile_components.py`** - Component isolation profiling (protobuf, registry, etc.)
15+
5. **`profiling_analysis.md`** - Comprehensive analysis of performance findings
16+
17+
### Generated Reports
18+
19+
- **CSV Reports**: Quantitative performance data in `profiling_results/*/profiling_summary_*.csv`
20+
- **Profile Files**: Detailed cProfile outputs (`.prof` files) for snakeviz analysis
21+
- **Memory Analysis**: Tracemalloc snapshots for memory usage patterns
22+
23+
## Key Performance Findings
24+
25+
### Major Bottlenecks Identified
26+
27+
1. **FeatureStore Initialization: 2.4-2.5 seconds**
28+
- Primary bottleneck for serverless deployments
29+
- Heavy import and dependency loading overhead
30+
- 99.8% of initialization time spent in `feature_store.py:123(__init__)`
31+
32+
2. **On-Demand Feature Views: 4x Performance Penalty**
33+
- Standard features: ~2ms per request
34+
- With ODFVs: ~8ms per request
35+
- Bottleneck: `on_demand_feature_view.py:819(transform_arrow)`
36+
37+
3. **Feature Services: 129% Overhead vs Direct Features**
38+
- Direct features: 7ms
39+
- Feature service: 16ms
40+
- Additional registry traversal costs
41+
42+
### Scaling Characteristics
43+
44+
- **Entity Count**: Linear scaling (good)
45+
- 1 entity: 2ms
46+
- 1000 entities: 22ms
47+
- **Memory Usage**: Efficient (<1MB for most operations)
48+
- **Provider Abstraction**: Minimal overhead
49+
50+
## Usage Instructions
51+
52+
### Quick Start
53+
54+
```bash
55+
# Run basic FeatureStore profiling
56+
python profile_feature_store.py
57+
58+
# Run component isolation tests
59+
python profile_components.py
60+
61+
# For FastAPI server profiling (requires additional deps):
62+
pip install requests aiohttp
63+
python profile_feature_server.py
64+
```
65+
66+
### Custom Profiling
67+
68+
```python
69+
from profiling_utils import FeastProfiler
70+
from feast import FeatureStore
71+
72+
profiler = FeastProfiler("my_results")
73+
74+
with profiler.profile_context("my_test") as result:
75+
store = FeatureStore(repo_path=".")
76+
77+
with profiler.time_operation("feature_retrieval", result):
78+
response = store.get_online_features(...)
79+
80+
# Add custom metrics
81+
result.add_timing("custom_metric", some_value)
82+
83+
# Generate reports
84+
profiler.print_summary()
85+
profiler.generate_csv_report()
86+
```
87+
88+
### Analysis Tools
89+
90+
```bash
91+
# View interactive call graphs
92+
pip install snakeviz
93+
snakeviz profiling_results/components/my_test_*.prof
94+
95+
# Analyze CSV reports
96+
import pandas as pd
97+
df = pd.read_csv("profiling_results/*/profiling_summary_*.csv")
98+
```
99+
100+
## Optimization Priorities
101+
102+
### High Impact (>100ms improvement potential)
103+
104+
1. **Optimize FeatureStore initialization** - Lazy loading, import optimization
105+
2. **On-Demand Feature View optimization** - Arrow operations, vectorization
106+
107+
### Medium Impact (10-100ms improvement potential)
108+
109+
3. **Entity batch processing** - Vectorized operations for large batches
110+
4. **Response serialization** - Streaming, protobuf optimization
111+
112+
### Low Impact (<10ms improvement potential)
113+
114+
5. **Registry operations** - Already efficient, minor optimizations possible
115+
116+
## Environment Setup
117+
118+
This profiling was conducted with:
119+
- **Data**: Local SQLite online store, 15 days × 5 drivers hourly stats
120+
- **Features**: Standard numerical features + on-demand transformations
121+
- **Scale**: 1-1000 entities, 1-5 features per request
122+
- **Provider**: Local SQLite (provider-agnostic bottlenecks identified)
123+
124+
## Production Recommendations
125+
126+
### For High-Throughput Serving
127+
128+
1. **Pre-initialize FeatureStore** - Keep warm instances to avoid 2.4s cold start
129+
2. **Minimize ODFV usage** - Consider pre-computation for performance-critical paths
130+
3. **Use direct feature lists** - Avoid feature service overhead when possible
131+
4. **Batch entity requests** - Linear scaling makes batching efficient
132+
133+
### For Serverless Deployment
134+
135+
1. **Investigate initialization optimization** - Biggest impact for cold starts
136+
2. **Consider connection pooling** - Reduce per-request overhead
137+
3. **Monitor memory usage** - Current usage is efficient (<1MB typical)
138+
139+
### For Development
140+
141+
1. **Use profiling suite** - Regular performance regression testing
142+
2. **Benchmark new features** - Especially ODFV implementations
143+
3. **Monitor provider changes** - Verify abstraction layer efficiency
144+
145+
## Next Steps
146+
147+
1. **Run FastAPI server profiling** with proper dependencies
148+
2. **Implement optimization recommendations** starting with high-impact items
149+
3. **Establish continuous profiling** in CI/CD pipeline
150+
4. **Profile production workloads** to validate findings
151+
152+
This profiling suite provides the foundation for ongoing Feast performance optimization and monitoring.

feast_profile_demo/feature_repo/__init__.py

Whitespace-only changes.
34.3 KB
Binary file not shown.
28 KB
Binary file not shown.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# This is an example feature definition file
2+
3+
from datetime import timedelta
4+
5+
import pandas as pd
6+
7+
from feast import (
8+
Entity,
9+
FeatureService,
10+
FeatureView,
11+
Field,
12+
FileSource,
13+
Project,
14+
PushSource,
15+
RequestSource,
16+
)
17+
from feast.feature_logging import LoggingConfig
18+
from feast.infra.offline_stores.file_source import FileLoggingDestination
19+
from feast.on_demand_feature_view import on_demand_feature_view
20+
from feast.types import Float32, Float64, Int64
21+
22+
# Define a project for the feature repo
23+
project = Project(name="feast_profile_demo", description="A project for driver statistics")
24+
25+
# Define an entity for the driver. You can think of an entity as a primary key used to
26+
# fetch features.
27+
driver = Entity(name="driver", join_keys=["driver_id"])
28+
29+
# Read data from parquet files. Parquet is convenient for local development mode. For
30+
# production, you can use your favorite DWH, such as BigQuery. See Feast documentation
31+
# for more info.
32+
driver_stats_source = FileSource(
33+
name="driver_hourly_stats_source",
34+
path="data/driver_stats.parquet",
35+
timestamp_field="event_timestamp",
36+
created_timestamp_column="created",
37+
)
38+
39+
# Our parquet files contain sample data that includes a driver_id column, timestamps and
40+
# three feature column. Here we define a Feature View that will allow us to serve this
41+
# data to our model online.
42+
driver_stats_fv = FeatureView(
43+
# The unique name of this feature view. Two feature views in a single
44+
# project cannot have the same name
45+
name="driver_hourly_stats",
46+
entities=[driver],
47+
ttl=timedelta(days=1),
48+
# The list of features defined below act as a schema to both define features
49+
# for both materialization of features into a store, and are used as references
50+
# during retrieval for building a training dataset or serving features
51+
schema=[
52+
Field(name="conv_rate", dtype=Float32),
53+
Field(name="acc_rate", dtype=Float32),
54+
Field(name="avg_daily_trips", dtype=Int64, description="Average daily trips"),
55+
],
56+
online=True,
57+
source=driver_stats_source,
58+
# Tags are user defined key/value pairs that are attached to each
59+
# feature view
60+
tags={"team": "driver_performance"},
61+
)
62+
63+
# Define a request data source which encodes features / information only
64+
# available at request time (e.g. part of the user initiated HTTP request)
65+
input_request = RequestSource(
66+
name="vals_to_add",
67+
schema=[
68+
Field(name="val_to_add", dtype=Int64),
69+
Field(name="val_to_add_2", dtype=Int64),
70+
],
71+
)
72+
73+
74+
# Define an on demand feature view which can generate new features based on
75+
# existing feature views and RequestSource features
76+
@on_demand_feature_view(
77+
sources=[driver_stats_fv, input_request],
78+
schema=[
79+
Field(name="conv_rate_plus_val1", dtype=Float64),
80+
Field(name="conv_rate_plus_val2", dtype=Float64),
81+
],
82+
)
83+
def transformed_conv_rate(inputs: pd.DataFrame) -> pd.DataFrame:
84+
df = pd.DataFrame()
85+
df["conv_rate_plus_val1"] = inputs["conv_rate"] + inputs["val_to_add"]
86+
df["conv_rate_plus_val2"] = inputs["conv_rate"] + inputs["val_to_add_2"]
87+
return df
88+
89+
90+
# This groups features into a model version
91+
driver_activity_v1 = FeatureService(
92+
name="driver_activity_v1",
93+
features=[
94+
driver_stats_fv[["conv_rate"]], # Sub-selects a feature from a feature view
95+
transformed_conv_rate, # Selects all features from the feature view
96+
],
97+
logging_config=LoggingConfig(
98+
destination=FileLoggingDestination(path="data")
99+
),
100+
)
101+
driver_activity_v2 = FeatureService(
102+
name="driver_activity_v2", features=[driver_stats_fv, transformed_conv_rate]
103+
)
104+
105+
# Defines a way to push data (to be available offline, online or both) into Feast.
106+
driver_stats_push_source = PushSource(
107+
name="driver_stats_push_source",
108+
batch_source=driver_stats_source,
109+
)
110+
111+
# Defines a slightly modified version of the feature view from above, where the source
112+
# has been changed to the push source. This allows fresh features to be directly pushed
113+
# to the online store for this feature view.
114+
driver_stats_fresh_fv = FeatureView(
115+
name="driver_hourly_stats_fresh",
116+
entities=[driver],
117+
ttl=timedelta(days=1),
118+
schema=[
119+
Field(name="conv_rate", dtype=Float32),
120+
Field(name="acc_rate", dtype=Float32),
121+
Field(name="avg_daily_trips", dtype=Int64),
122+
],
123+
online=True,
124+
source=driver_stats_push_source, # Changed from above
125+
tags={"team": "driver_performance"},
126+
)
127+
128+
129+
# Define an on demand feature view which can generate new features based on
130+
# existing feature views and RequestSource features
131+
@on_demand_feature_view(
132+
sources=[driver_stats_fresh_fv, input_request], # relies on fresh version of FV
133+
schema=[
134+
Field(name="conv_rate_plus_val1", dtype=Float64),
135+
Field(name="conv_rate_plus_val2", dtype=Float64),
136+
],
137+
)
138+
def transformed_conv_rate_fresh(inputs: pd.DataFrame) -> pd.DataFrame:
139+
df = pd.DataFrame()
140+
df["conv_rate_plus_val1"] = inputs["conv_rate"] + inputs["val_to_add"]
141+
df["conv_rate_plus_val2"] = inputs["conv_rate"] + inputs["val_to_add_2"]
142+
return df
143+
144+
145+
driver_activity_v3 = FeatureService(
146+
name="driver_activity_v3",
147+
features=[driver_stats_fresh_fv, transformed_conv_rate_fresh],
148+
)

0 commit comments

Comments
 (0)